1 /*
2 * This file is part of the openHiTLS project.
3 *
4 * openHiTLS is licensed under the Mulan PSL v2.
5 * You can use this software according to the terms and conditions of the Mulan PSL v2.
6 * You may obtain a copy of Mulan PSL v2 at:
7 *
8 * http://license.coscl.org.cn/MulanPSL2
9 *
10 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13 * See the Mulan PSL v2 for more details.
14 */
15
16 #include <errno.h>
17 #include <limits.h>
18 #include <memory.h>
19 #include <stdint.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <stdbool.h>
24 #include <sys/mman.h>
25 #include "stub_replace.h"
26
27 #ifdef HITLS_BIG_ENDIAN
28 #include "crypt_utils.h"
29 #endif
30
31 /* The LSB of the function pointer indicates the thumb function. The LSB of the actual address needs to be cleared. */
32 #define REAL_ADDR(ptr) (void *)(((uintptr_t)(ptr)) & (~(uintptr_t)1))
33 /*
34 * Used to record the size of the system memory page.
35 */
36 static long g_pageSize = -1;
37
38 /*
39 * Obtains the start address of the memory page where the specified function code is located.
40 * fn - Function Address (Function Pointer)
41 */
FuncPageGet(uintptr_t fn)42 static inline void *FuncPageGet(uintptr_t fn)
43 {
44 return (void *)(fn & (~(g_pageSize - 1)));
45 }
46
47 /*
48 * This file does not use the memcpy function of the system. In this way, the mempcy function of
49 * the system can be dynamically replaced by STUB_Replace.
50 */
StubCopy(void * dest,void * src,uint32_t size)51 static int32_t StubCopy(void *dest, void *src, uint32_t size)
52 {
53 if ((src == NULL) || (dest == NULL)) {
54 return ERANGE;
55 }
56
57 uint8_t *localDst = (uint8_t *)(dest);
58 uint8_t *localSrc = (uint8_t *)(src);
59 for (uint32_t i = 0; i < size; i++) {
60 localDst[i] = localSrc[i];
61 }
62 return 0;
63 }
64
65 /*
66 * This file does not use the memset function of the system. In this way, the memset function of
67 * the system can be dynamically replaced by STUB_Replace.
68 */
StubSet(void * dest,int val,uint32_t size)69 static void StubSet(void *dest, int val, uint32_t size)
70 {
71 if (dest == NULL) {
72 return;
73 }
74
75 uint8_t *localDst = (uint8_t *)(dest);
76 for (uint32_t i = 0; i < size; i++) {
77 localDst[i] = val;
78 }
79 }
80
81 #if defined(__arm__) || defined(__thumb__)
82
ReplaceT32(void * srcFn,const void * stubFn)83 static int ReplaceT32(void *srcFn, const void *stubFn)
84 {
85 uint16_t instr1 = 0xF000;
86 uint16_t instr2;
87 uint32_t imm;
88 /*
89 * The difference between the jump instruction and srcFn is 4 bytes. The current address is obtained by
90 * subtracting 4 bytes from the PC.
91 */
92 uint32_t addrDiff = REAL_ADDR(stubFn) - (REAL_ADDR(srcFn) + 4) - 4;
93
94 /*
95 * 32-bit test occurrence address: srcFn - stubFn, the scope is out of range,
96 * temporarily comment on the following scope judgment.
97 * if (abs((int32_t)addrDiff) >= 0x100000) { // Max jump range
98 * return -1;
99 * }
100 */
101 if (((uintptr_t)stubFn) & 0x01) {
102 // Thumb instruction set BL corresponding machine code is [1 1 1 1 0 S imm10][1 1 J1 1 J2 imm11]
103 // Address offset calculation: I1: NOT(J1 EOR S); I2: NOT(J2 EOR S);
104 // imm32: SignExtend(S:I1:I2:imm10:imm11:'0', 32)
105 instr2 = 0xF800; // Corresponding bit of machine code J1 J2 take 1
106 if (stubFn < srcFn) {
107 instr1 = 0xF400; // The corresponding bit S of the machine code is 1.
108 }
109 imm = addrDiff >> 1; // The address is shifted right by one bit.
110 imm &= (1 << 21) - 1; // Lower 21 bits
111 instr1 |= (imm >> 11) & 0x3FF; // Move rightwards by 11 digits and take imm10.
112 instr2 |= (imm & 0x7FF);
113 } else {
114 // Thumb instruction set BLX corresponding machine code is [1 1 1 1 0 S imm10H][1 1 J1 0 J2 imm10L H]
115 // Address offset calculation: I1 = NOT(J1 EOR S); I2 = NOT(J2 EOR S)
116 // imm32 = SignExtend(S:I1:I2:imm10H:imm10L:'00', 32)
117 instr2 = 0xE800; // J1 and J2 corresponding to the machine code are set to 1.
118 if (stubFn < srcFn) {
119 instr1 = 0xF400; // The corresponding bit S of the machine code is 1.
120 }
121 imm = addrDiff >> 2; // Shift right by 2 bits
122 imm &= (1 << 20) - 1; // Take lower 20 bits
123 instr1 |= (imm >> 10) & 0x3FF; // Take 10 bits
124 instr2 |= (imm & 0x3FF) << 1; // Take lower 10 bits
125 }
126 uint8_t *text = (uint8_t*)REAL_ADDR(srcFn);
127 ((uint16_t *)text)[0] = 0xb580;
128 ((uint16_t *)text)[1] = 0xaf00;
129 ((uint16_t *)text)[2] = instr1; // BL/BLX offset 2
130 ((uint16_t *)text)[3] = instr2; // Offset 3
131 ((uint16_t *)text)[4] = 0xaf00; // Offset 4
132 ((uint16_t *)text)[5] = 0xbd80; // Offset 5
133 return 0;
134 }
135
ReplaceA32(void * srcFn,const void * stubFn)136 static int ReplaceA32(void *srcFn, const void *stubFn)
137 {
138 uint32_t inst;
139 uint32_t addrDiff = REAL_ADDR(stubFn) - (srcFn + 4) - 8;
140 uint32_t imm24;
141 if (abs((int32_t)addrDiff) >= 0x1000000) { // Max jump range
142 return -1;
143 }
144 if (((uintptr_t)stubFn) & 0x01) {
145 // a32 instruction set BLX corresponding machine code is [1 1 1 1 1 0 1 H imm24]
146 // imm32 = SignExtend(imm24:H:'0', 32)
147 uint32_t h = (addrDiff & 0b10) >> 1; // bit[1] of the address difference
148 imm24 = (addrDiff >> 2); // Shift right by 2 bits
149 imm24 &= (1 << 24) - 1; // Take lower 24 bits
150 inst = 0xfa000000 | imm24 | (h << 24); // h is located in bit[24].
151 } else {
152 // a32 instruction set BL corresponding machine code is [(!= 1111) 1 0 1 1 imm24]
153 // imm32 = SignExtend(imm24:'00', 32)
154 imm24 = (addrDiff >> 2); // Shift right by 2 bits
155 imm24 &= (1 << 24) - 1; // Take lower 24 bits
156 inst = 0xeb000000 | imm24;
157 }
158 ((uint32_t *)srcFn)[0] = 0xe92d4000;
159 ((uint32_t *)srcFn)[1] = inst; // BL/BLX
160 ((uint32_t *)srcFn)[2] = 0xe8bd8000; // Offset 2
161 return 0;
162 }
163 #endif
164 /*
165 * Replaces the specified function with the specified stub function.
166 * stubInfo - Record information about stub replacement, which is used for STUB_Reset restoration.
167 * srcFn - Functions in the source code
168 * stubFn - You need to replace the stub function that is inserted into the run.
169 * return - 0:Success, non-zero:Error code
170 */
STUB_Replace(FuncStubInfo * stubInfo,void * srcFn,const void * stubFn)171 int STUB_Replace(FuncStubInfo *stubInfo, void *srcFn, const void *stubFn)
172 {
173 (void)stubFn;
174 #if defined(__arm__) || defined(__thumb__)
175 stubInfo->fn = REAL_ADDR(srcFn);
176 #else
177 stubInfo->fn = srcFn;
178 #endif
179 StubCopy(stubInfo->codeBuf, (char *)(stubInfo->fn), CODESIZE);
180 bool nextPage = false;
181 uintptr_t srcPoint = (uintptr_t)srcFn;
182 if ((g_pageSize - (srcPoint % g_pageSize)) < CODESIZE) {
183 nextPage = true;
184 }
185
186 /* To modify instruction content corresponding to the source function, add memory write permission first */
187 if (mprotect(FuncPageGet(srcPoint), g_pageSize, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) {
188 perror("STUB_Replace: set error mprotect to w+r+x faild");
189 return -1;
190 }
191 if (nextPage) {
192 if (mprotect(FuncPageGet(srcPoint + CODESIZE), g_pageSize, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) {
193 perror("STUB_Replace: set error mprotect to w+r+x faild");
194 return -1;
195 }
196 }
197 #if defined(__x86_64__)
198 /*
199 * Short jump mode: Change to jmp jump instruction, and set jump position (the offset of the current position).
200 * However, the offset cannot exceed 32 bits. There is a restriction on 64-bit systems.
201 * [*(unsigned char *)srcFn = (unsigned char)0xE9;] [*(unsigned int *)((unsigned char *)srcFn + 1) =
202 * (unsigned char *)stubFn - (unsigned char *)srcFn - CODESIZE;] Long jump mode:
203 * Directly use a 64-bit address to jump, the following method is used.
204 */
205 unsigned char *tmpBuf = (unsigned char *)srcFn;
206 int idx = 0;
207 tmpBuf[idx++] = 0xFF; // 0xFF 0x25 Constructing a long jump instruction
208 tmpBuf[idx++] = 0x25; // 0xFF 0x25 Constructing a long jump instruction
209 tmpBuf[idx++] = 0x0;
210 tmpBuf[idx++] = 0x0;
211 tmpBuf[idx++] = 0x0;
212 tmpBuf[idx++] = 0x0;
213
214 tmpBuf[idx++] = (((uintptr_t)stubFn) & 0xff);
215 tmpBuf[idx++] = ((((uintptr_t)stubFn) >> 8) & 0xff); // Obtain the address by little-endian shift by 8 bits
216 tmpBuf[idx++] = ((((uintptr_t)stubFn) >> 16) & 0xff); // Obtain the address by little-endian shift by 16 bits
217 tmpBuf[idx++] = ((((uintptr_t)stubFn) >> 24) & 0xff); // Obtain the address by little-endian shift by 24 bits
218 tmpBuf[idx++] = ((((uintptr_t)stubFn) >> 32) & 0xff); // Obtain the address by little-endian shift by 32 bits
219 tmpBuf[idx++] = ((((uintptr_t)stubFn) >> 40) & 0xff); // Obtain the address by little-endian shift by 40 bits
220 tmpBuf[idx++] = ((((uintptr_t)stubFn) >> 48) & 0xff); // Obtain the address by little-endian shift by 48 bits
221 tmpBuf[idx++] = ((((uintptr_t)stubFn) >> 56) & 0xff); // Obtain the address by little-endian shift by 56 bits
222 #elif defined(__aarch64__) || defined(_M_ARM64)
223 /* ldr x9, PC+8
224 br x9
225 addr */
226 uint32_t ldrIns = 0x58000040 | 9; // 9 = 1001
227 uint32_t brIns = 0xd61f0120 | (9 << 5); // 9 << 5
228 #ifdef HITLS_BIG_ENDIAN
229 ldrIns = CRYPT_SWAP32(ldrIns);
230 brIns = CRYPT_SWAP32(brIns);
231 #endif
232 ((uint32_t *)srcFn)[0] = ldrIns;
233 ((uint32_t *)srcFn)[1] = brIns;
234 /* ldr x9, + 8 */
235 *(long long *)((char *)srcFn + 8) = (long long)stubFn;
236 #elif defined(__arm__) || defined(__thumb__)
237 if (((uintptr_t)srcFn) & 0x01) {
238 if (ReplaceT32(srcFn, stubFn) != 0) {
239 return -1;
240 }
241 } else {
242 if (ReplaceA32(srcFn, stubFn) != 0) {
243 return -1;
244 }
245 }
246 #elif defined(__i386__)
247 unsigned long tmpAdd = (unsigned long)stubFn - (unsigned long)(srcFn + 5);
248 unsigned char *tmpBuf = (unsigned char *)srcFn;
249 *(tmpBuf + 0) = 0xe9;
250 *(unsigned long *)(tmpBuf + 1) = tmpAdd;
251 #endif
252 /* Flush cached instructions into */
253 __builtin___clear_cache((char *)(stubInfo->fn), (char *)(stubInfo->fn) + CODESIZE);
254 /* The modification is complete. Remove the memory write permission. */
255 if (mprotect(FuncPageGet(srcPoint), g_pageSize, PROT_READ | PROT_EXEC) < 0) {
256 perror("STUB_Replace: set error mprotect to r+x failed");
257 return -1;
258 }
259 if (nextPage) {
260 if (mprotect(FuncPageGet(srcPoint + CODESIZE), g_pageSize, PROT_READ | PROT_EXEC) < 0) {
261 perror("STUB_Replace: set error mprotect to r+x failed");
262 return -1;
263 }
264 }
265 return 0;
266 }
267
268 /*
269 * Restore the source function and remove the instrumentation.
270 * stubInfo - Information logged when instrumentation
271 * return - 0:Success, non-zero:Error code
272 */
STUB_Reset(FuncStubInfo * stubInfo)273 int STUB_Reset(FuncStubInfo *stubInfo)
274 {
275 bool nextPage = false;
276
277 if (stubInfo->fn == NULL) {
278 return -1;
279 }
280
281 uintptr_t srcPoint = (uintptr_t)stubInfo->fn;
282 if ((g_pageSize - (srcPoint % g_pageSize)) < CODESIZE) {
283 nextPage = true;
284 }
285
286 /* To modify instruction content corresponding to the source function, add memory write permission first */
287 if (mprotect(FuncPageGet((uintptr_t)stubInfo->fn), g_pageSize, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) {
288 perror("STUB_Reset: error mprotect to w+r+x faild");
289 return -1;
290 }
291 if (nextPage) {
292 if (mprotect(FuncPageGet(srcPoint + CODESIZE), g_pageSize, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) {
293 perror("STUB_Replace: set error mprotect to w+r+x faild");
294 return -1;
295 }
296 }
297
298 /* Restore the recorded rewritten original function mov/push/mov a few instructions */
299 if (StubCopy(stubInfo->fn, stubInfo->codeBuf, CODESIZE) < 0) {
300 return -1;
301 }
302 /* Flush cached instructions into */
303 __builtin___clear_cache((char *)stubInfo->fn, (char *)stubInfo->fn + CODESIZE);
304 /* If recovered, disable the memory modification permission. */
305 if (mprotect(FuncPageGet((uintptr_t)stubInfo->fn), g_pageSize, PROT_READ | PROT_EXEC) < 0) {
306 perror("STUB_Reset: error mprotect to r+x failed");
307 return -1;
308 }
309 if (nextPage) {
310 if (mprotect(FuncPageGet(srcPoint + CODESIZE), g_pageSize, PROT_READ | PROT_EXEC) < 0) {
311 perror("STUB_Replace: set error mprotect to r+x failed");
312 return -1;
313 }
314 }
315
316 StubSet(stubInfo, 0, sizeof(FuncStubInfo));
317 return 0;
318 }
319
320 /*
321 * Initialize the dynamic stub change function and obtain the memory page size. Invoke the function once.
322 * return - 0:Success, non-zero:Error code
323 */
STUB_Init(void)324 int STUB_Init(void)
325 {
326 if (g_pageSize != -1) {
327 return 0;
328 }
329 g_pageSize = sysconf(_SC_PAGE_SIZE);
330 if (g_pageSize < 0) {
331 perror("STUB_Init: get system _SC_PAGE_SIZE configure failed");
332 return -1;
333 }
334 return 0;
335 }
336