1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "./shell-code.h"
18 
19 #include <sys/mman.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22 
23 // Shell code that sets the SELinux context of the current process.
24 //
25 // The shell code expects a null-terminated SELinux context string to be placed
26 // immediately after it in memory. After the SELinux context has been changed
27 // the shell code will stop the current process with SIGSTOP.
28 //
29 // This shell code must be self-contained and position-independent.
30 extern "C" void __setcon_shell_code_start();
31 extern "C" void __setcon_shell_code_end();
32 
33 // Shell code that stops execution of the current process by raising a signal.
34 // The specific signal that is raised is given in __trap_shell_code_signal.
35 //
36 // This shell code can be used to inject break points into a traced process.
37 //
38 // The shell code must not modify any registers other than the program counter.
39 extern "C" void __trap_shell_code_start();
40 extern "C" void __trap_shell_code_end();
41 extern "C" int __trap_shell_code_signal;
42 
43 namespace shell_as {
44 
45 namespace {
PageStart(void (* addr)())46 uintptr_t PageStart(void (*addr)()) {
47   return ((uintptr_t)addr & ~(getpagesize() - 1));
48 }
49 
EnsureShellcodeReadable(void (* start)(),void (* end)())50 void EnsureShellcodeReadable(void (*start)(), void (*end)()) {
51   mprotect((void*)PageStart(start),
52            PageStart(end) - PageStart(start) + getpagesize(),
53            PROT_READ | PROT_EXEC);
54 }
55 }  // namespace
56 
GetSELinuxShellCode(char * selinux_context,size_t * total_size)57 std::unique_ptr<uint8_t[]> GetSELinuxShellCode(
58     char* selinux_context, size_t* total_size) {
59   EnsureShellcodeReadable(&__setcon_shell_code_start, &__setcon_shell_code_end);
60 
61   size_t shell_code_size = (uintptr_t)&__setcon_shell_code_end -
62                            (uintptr_t)&__setcon_shell_code_start;
63   size_t selinux_context_size = strlen(selinux_context) + 1 /* null byte */;
64   *total_size = shell_code_size + selinux_context_size;
65 
66   std::unique_ptr<uint8_t[]> shell_code(new uint8_t[*total_size]);
67   memcpy(shell_code.get(), (void*)&__setcon_shell_code_start, shell_code_size);
68   memcpy(shell_code.get() + shell_code_size, selinux_context,
69          selinux_context_size);
70   return shell_code;
71 }
72 
GetTrapShellCode(int * expected_signal,size_t * total_size)73 std::unique_ptr<uint8_t[]> GetTrapShellCode(int* expected_signal,
74                                             size_t* total_size) {
75   EnsureShellcodeReadable(&__trap_shell_code_start, &__trap_shell_code_end);
76 
77   *expected_signal = __trap_shell_code_signal;
78 
79   *total_size =
80       (uintptr_t)&__trap_shell_code_end - (uintptr_t)&__trap_shell_code_start;
81   std::unique_ptr<uint8_t[]> shell_code(new uint8_t[*total_size]);
82   memcpy(shell_code.get(), (void*)&__trap_shell_code_start, *total_size);
83   return shell_code;
84 }
85 }  // namespace shell_as
86