1 /*
2  * Copyright (C) 2024 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 <libelf64/elf64.h>
18 #include <libelf64/parse.h>
19 #include <libelf64/writer.h>
20 
21 #include <iostream>
22 #include <set>
23 #include <string>
24 #include <vector>
25 
26 #include <elf.h>
27 #include <stdlib.h>
28 
29 // Remove the sharedLibs from the .dynamic section.
30 // In order to remove the sharedLibs from the .dynamic
31 // section, it sets the Elf64_Dyn.d_tag to DT_DEBUG.
remove_needed_shared_libs(android::elf64::Elf64Binary & elf64Binary,const std::set<std::string> & sharedLibs)32 void remove_needed_shared_libs(android::elf64::Elf64Binary& elf64Binary,
33                                const std::set<std::string>& sharedLibs) {
34     std::vector<Elf64_Dyn> dynEntries;
35 
36     elf64Binary.AppendDynamicEntries(&dynEntries);
37 
38     for (int i = 0; i < dynEntries.size(); i++) {
39         if (dynEntries[i].d_tag == DT_NEEDED) {
40             std::string libName = elf64Binary.GetStrFromDynStrTable(dynEntries[i].d_un.d_val);
41 
42             if (sharedLibs.count(libName)) {
43                 dynEntries[i].d_tag = DT_DEBUG;
44             }
45         }
46     }
47 
48     elf64Binary.SetDynamicEntries(&dynEntries);
49 }
50 
set_exec_segments_as_rwx(android::elf64::Elf64Binary & elf64Binary)51 void set_exec_segments_as_rwx(android::elf64::Elf64Binary& elf64Binary) {
52     for (int i = 0; i < elf64Binary.phdrs.size(); i++) {
53         if (elf64Binary.phdrs[i].p_flags & PF_X) {
54             elf64Binary.phdrs[i].p_flags |= PF_W;
55         }
56     }
57 }
58 
59 // Generates a shared library with the executable segments as read/write/exec.
gen_lib_with_rwx_segment(const android::elf64::Elf64Binary & elf64Binary,const std::string & newSharedLibName)60 void gen_lib_with_rwx_segment(const android::elf64::Elf64Binary& elf64Binary,
61                               const std::string& newSharedLibName) {
62     android::elf64::Elf64Binary copyElf64Binary = elf64Binary;
63     set_exec_segments_as_rwx(copyElf64Binary);
64     android::elf64::Elf64Writer::WriteElf64File(copyElf64Binary, newSharedLibName);
65 }
66 
67 // Generates a shared library with the size of the section headers as zero.
gen_lib_with_zero_shentsize(const android::elf64::Elf64Binary & elf64Binary,const std::string & newSharedLibName)68 void gen_lib_with_zero_shentsize(const android::elf64::Elf64Binary& elf64Binary,
69                                  const std::string& newSharedLibName) {
70     android::elf64::Elf64Binary copyElf64Binary = elf64Binary;
71 
72     copyElf64Binary.ehdr.e_shentsize = 0;
73     android::elf64::Elf64Writer::WriteElf64File(copyElf64Binary, newSharedLibName);
74 }
75 
76 // Generates a shared library with invalid section header string table index.
gen_lib_with_zero_shstrndx(const android::elf64::Elf64Binary & elf64Binary,const std::string & newSharedLibName)77 void gen_lib_with_zero_shstrndx(const android::elf64::Elf64Binary& elf64Binary,
78                                 const std::string& newSharedLibName) {
79     android::elf64::Elf64Binary copyElf64Binary = elf64Binary;
80 
81     copyElf64Binary.ehdr.e_shstrndx = 0;
82     android::elf64::Elf64Writer::WriteElf64File(copyElf64Binary, newSharedLibName);
83 }
84 
85 // Generates a shared library with text relocations set in DT_FLAGS dynamic
86 // entry. For example:
87 //
88 //  $ readelf -d libtest_invalid-textrels.so | grep TEXTREL
89 //  0x000000000000001e (FLAGS)              TEXTREL BIND_NOW
gen_lib_with_text_relocs_in_flags(const android::elf64::Elf64Binary & elf64Binary,const std::string & newSharedLibName)90 void gen_lib_with_text_relocs_in_flags(const android::elf64::Elf64Binary& elf64Binary,
91                                        const std::string& newSharedLibName) {
92     android::elf64::Elf64Binary copyElf64Binary = elf64Binary;
93     std::vector<Elf64_Dyn> dynEntries;
94     bool found = false;
95 
96     copyElf64Binary.AppendDynamicEntries(&dynEntries);
97     for (int i = 0; i < dynEntries.size(); i++) {
98         if (dynEntries[i].d_tag == DT_FLAGS) {
99             // Indicate that binary contains text relocations.
100             dynEntries[i].d_un.d_val |= DF_TEXTREL;
101             found = true;
102             break;
103         }
104     }
105 
106     if (!found) {
107         std::cerr << "Unable to set text relocations in DT_FLAGS. File " << newSharedLibName
108                   << " not created." << std::endl;
109         return;
110     }
111 
112     copyElf64Binary.SetDynamicEntries(&dynEntries);
113     android::elf64::Elf64Writer::WriteElf64File(copyElf64Binary, newSharedLibName);
114 }
115 
116 // Generates a shared library with a DT_TEXTREL dynamic entry.
117 // For example:
118 //
119 // $ readelf -d arm64/libtest_invalid-textrels2.so  | grep TEXTREL
120 // 0x0000000000000016 (TEXTREL)            0x0
gen_lib_with_text_relocs_dyn_entry(const android::elf64::Elf64Binary & elf64Binary,const std::string & newSharedLibName)121 void gen_lib_with_text_relocs_dyn_entry(const android::elf64::Elf64Binary& elf64Binary,
122                                         const std::string& newSharedLibName) {
123     android::elf64::Elf64Binary copyElf64Binary = elf64Binary;
124     std::vector<Elf64_Dyn> dynEntries;
125     bool found = false;
126 
127     copyElf64Binary.AppendDynamicEntries(&dynEntries);
128     for (int i = 0; i < dynEntries.size(); i++) {
129         if (dynEntries[i].d_tag == DT_FLAGS) {
130             dynEntries[i].d_tag = DT_TEXTREL;
131             found = true;
132             break;
133         }
134     }
135 
136     if (!found) {
137         std::cerr << "Unable to create shared library with DT_TEXTREL dynamic entry. File "
138                   << newSharedLibName << " not created." << std::endl;
139         return;
140     }
141 
142     copyElf64Binary.SetDynamicEntries(&dynEntries);
143     android::elf64::Elf64Writer::WriteElf64File(copyElf64Binary, newSharedLibName);
144 }
145 
146 // Generates a shared library which executable header indicates that there
147 // are ZERO section headers.
148 //
149 // For example:
150 //
151 // $ readelf -h libtest_invalid-empty_shdr_table.so | grep Number
152 // Number of program headers:         8
153 // Number of section headers:         0 (0)
gen_lib_with_empty_shdr_table(const android::elf64::Elf64Binary & elf64Binary,const std::string & newSharedLibName)154 void gen_lib_with_empty_shdr_table(const android::elf64::Elf64Binary& elf64Binary,
155                                    const std::string& newSharedLibName) {
156     android::elf64::Elf64Binary copyElf64Binary = elf64Binary;
157 
158     copyElf64Binary.ehdr.e_shnum = 0;
159     android::elf64::Elf64Writer::WriteElf64File(copyElf64Binary, newSharedLibName);
160 }
161 
set_shdr_table_offset(const android::elf64::Elf64Binary & elf64Binary,const std::string & newSharedLibName,const Elf64_Off invalidOffset)162 void set_shdr_table_offset(const android::elf64::Elf64Binary& elf64Binary,
163                            const std::string& newSharedLibName, const Elf64_Off invalidOffset) {
164     android::elf64::Elf64Binary copyElf64Binary = elf64Binary;
165 
166     // Set an invalid offset for the section headers.
167     copyElf64Binary.ehdr.e_shoff = invalidOffset;
168 
169     std::cout << "Writing ELF64 binary to file " << newSharedLibName << std::endl;
170     android::elf64::Elf64Writer elf64Writer(newSharedLibName);
171     elf64Writer.WriteHeader(copyElf64Binary.ehdr);
172     elf64Writer.WriteProgramHeaders(copyElf64Binary.phdrs, copyElf64Binary.ehdr.e_phoff);
173     elf64Writer.WriteSections(copyElf64Binary.sections, copyElf64Binary.shdrs);
174 
175     // Use the original e_shoff to store the section headers.
176     elf64Writer.WriteSectionHeaders(copyElf64Binary.shdrs, elf64Binary.ehdr.e_shoff);
177 }
178 
179 // Generates a shared library which executable header has an invalid
180 // section header offset.
gen_lib_with_unaligned_shdr_offset(const android::elf64::Elf64Binary & elf64Binary,const std::string & newSharedLibName)181 void gen_lib_with_unaligned_shdr_offset(const android::elf64::Elf64Binary& elf64Binary,
182                                         const std::string& newSharedLibName) {
183     const Elf64_Off unalignedOffset = elf64Binary.ehdr.e_shoff + 1;
184     set_shdr_table_offset(elf64Binary, newSharedLibName, unalignedOffset);
185 }
186 
187 // Generates a shared library which executable header has ZERO as
188 // section header offset.
gen_lib_with_zero_shdr_table_offset(const android::elf64::Elf64Binary & elf64Binary,const std::string & newSharedLibName)189 void gen_lib_with_zero_shdr_table_offset(const android::elf64::Elf64Binary& elf64Binary,
190                                          const std::string& newSharedLibName) {
191     const Elf64_Off zeroOffset = 0;
192     set_shdr_table_offset(elf64Binary, newSharedLibName, zeroOffset);
193 }
194 
195 // Generates a shared library which section headers are all ZERO.
gen_lib_with_zero_shdr_table_content(const android::elf64::Elf64Binary & elf64Binary,const std::string & newSharedLibName)196 void gen_lib_with_zero_shdr_table_content(const android::elf64::Elf64Binary& elf64Binary,
197                                           const std::string& newSharedLibName) {
198     android::elf64::Elf64Binary copyElf64Binary = elf64Binary;
199 
200     std::cout << "Writing ELF64 binary to file " << newSharedLibName << std::endl;
201     android::elf64::Elf64Writer elf64Writer(newSharedLibName);
202     elf64Writer.WriteHeader(copyElf64Binary.ehdr);
203     elf64Writer.WriteProgramHeaders(copyElf64Binary.phdrs, copyElf64Binary.ehdr.e_phoff);
204     elf64Writer.WriteSections(copyElf64Binary.sections, copyElf64Binary.shdrs);
205 
206     // Make the content of Elf64_Shdr zero.
207     for (int i = 0; i < copyElf64Binary.shdrs.size(); i++) {
208         copyElf64Binary.shdrs[i] = {0};
209     }
210 
211     elf64Writer.WriteSectionHeaders(copyElf64Binary.shdrs, elf64Binary.ehdr.e_shoff);
212 }
213 
usage()214 void usage() {
215     const std::string progname = getprogname();
216 
217     std::cout << "Usage: " << progname << " [shared_lib] [out_dir]...\n"
218               << R"(
219 Options:
220 shared_lib       elf64 shared library that will be used as reference.
221 out_dir          the invalid shared libraries that are
222                  generated will be placed in this directory.)"
223               << std::endl;
224 }
225 
226 // Generate shared libraries with invalid:
227 //
228 //   - executable header
229 //   - segment headers
230 //   - section headers
main(int argc,char * argv[])231 int main(int argc, char* argv[]) {
232     if (argc < 3) {
233         usage();
234         return EXIT_FAILURE;
235     }
236 
237     std::string baseSharedLibName(argv[1]);
238     std::string outputDir(argv[2]);
239 
240     android::elf64::Elf64Binary elf64Binary;
241     if (android::elf64::Elf64Parser::ParseElfFile(baseSharedLibName, elf64Binary)) {
242         std::set<std::string> libsToRemove = {"libc++_shared.so"};
243         remove_needed_shared_libs(elf64Binary, libsToRemove);
244 
245         gen_lib_with_rwx_segment(elf64Binary, outputDir + "/libtest_invalid-rw_load_segment.so");
246         gen_lib_with_zero_shentsize(elf64Binary, outputDir + "/libtest_invalid-zero_shentsize.so");
247         gen_lib_with_zero_shstrndx(elf64Binary, outputDir + "/libtest_invalid-zero_shstrndx.so");
248         gen_lib_with_text_relocs_in_flags(elf64Binary, outputDir + "/libtest_invalid-textrels.so");
249         gen_lib_with_text_relocs_dyn_entry(elf64Binary,
250                                            outputDir + "/libtest_invalid-textrels2.so");
251         gen_lib_with_empty_shdr_table(elf64Binary,
252                                       outputDir + "/libtest_invalid-empty_shdr_table.so");
253         gen_lib_with_unaligned_shdr_offset(elf64Binary,
254                                            outputDir + "/libtest_invalid-unaligned_shdr_offset.so");
255         gen_lib_with_zero_shdr_table_content(
256                 elf64Binary, outputDir + "/libtest_invalid-zero_shdr_table_content.so");
257         gen_lib_with_zero_shdr_table_offset(
258                 elf64Binary, outputDir + "/libtest_invalid-zero_shdr_table_offset.so");
259     }
260 
261     return 0;
262 }
263