• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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