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