1 // Copyright 2023 Code Intelligence GmbH
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <dlfcn.h>
16 #include <jni.h>
17
18 #include <fstream>
19 #include <iostream>
20 #include <map>
21 #include <memory>
22 #include <sstream>
23 #include <string>
24 #include <unordered_set>
25 #include <vector>
26
27 #include "absl/strings/str_split.h"
28 #include "dex_file_manager.h"
29 #include "jazzer_jvmti_allocator.h"
30 #include "jvmti.h"
31 #include "slicer/arrayview.h"
32 #include "slicer/dex_format.h"
33 #include "slicer/reader.h"
34 #include "slicer/writer.h"
35
36 static std::string agentOptions;
37 static DexFileManager dfm;
38
39 const std::string kAndroidAgentClass =
40 "com/code_intelligence/jazzer/android/DexFileManager";
41
retransformLoadedClasses(jvmtiEnv * jvmti,JNIEnv * env)42 void retransformLoadedClasses(jvmtiEnv* jvmti, JNIEnv* env) {
43 jint classCount = 0;
44 jclass* classes;
45
46 jvmti->GetLoadedClasses(&classCount, &classes);
47
48 std::vector<jclass> classesToRetransform;
49 for (int i = 0; i < classCount; i++) {
50 jboolean isModifiable = false;
51 jvmti->IsModifiableClass(classes[i], &isModifiable);
52
53 if ((bool)isModifiable) {
54 classesToRetransform.push_back(classes[i]);
55 }
56 }
57
58 jvmtiError errorNum = jvmti->RetransformClasses(classesToRetransform.size(),
59 &classesToRetransform[0]);
60 if (errorNum != JVMTI_ERROR_NONE) {
61 std::cerr << "Could not retransform classes. JVMTI error: " << errorNum
62 << std::endl;
63 exit(1);
64 }
65 }
66
getDexFiles(std::string jarPath,JNIEnv * env)67 std::vector<std::string> getDexFiles(std::string jarPath, JNIEnv* env) {
68 jclass jazzerClass = env->FindClass(kAndroidAgentClass.c_str());
69 if (jazzerClass == nullptr) {
70 std::cerr << kAndroidAgentClass << " could not be found" << std::endl;
71 exit(1);
72 }
73
74 const char* getDexFilesFunction = "getDexFilesForJar";
75 jmethodID getDexFilesForJar =
76 env->GetStaticMethodID(jazzerClass, getDexFilesFunction,
77 "(Ljava/lang/String;)[Ljava/lang/String;");
78 if (getDexFilesForJar == nullptr) {
79 std::cerr << getDexFilesFunction << " could not be found\n";
80 exit(1);
81 }
82
83 jstring jJarFile = env->NewStringUTF(jarPath.data());
84 jobjectArray dexFilesArray = (jobjectArray)env->CallStaticObjectMethod(
85 jazzerClass, getDexFilesForJar, jJarFile);
86
87 if (env->ExceptionCheck()) {
88 env->ExceptionDescribe();
89 exit(1);
90 }
91
92 int length = env->GetArrayLength(dexFilesArray);
93
94 std::vector<std::string> dexFilesResult;
95 for (int i = 0; i < length; i++) {
96 jstring dexFileJstring =
97 (jstring)env->GetObjectArrayElement(dexFilesArray, i);
98 const char* dexFileChars = env->GetStringUTFChars(dexFileJstring, NULL);
99 std::string dexFileString(dexFileChars);
100
101 env->ReleaseStringUTFChars(dexFileJstring, dexFileChars);
102 dexFilesResult.push_back(dexFileString);
103 }
104
105 return dexFilesResult;
106 }
107
initializeBootclassOverrideJar(std::string jarPath,JNIEnv * env)108 void initializeBootclassOverrideJar(std::string jarPath, JNIEnv* env) {
109 std::vector<std::string> dexFiles = getDexFiles(jarPath, env);
110
111 std::cerr << "Adding DEX files for: " << jarPath << std::endl;
112 for (int i = 0; i < dexFiles.size(); i++) {
113 std::cerr << "DEX FILE: " << dexFiles[i] << std::endl;
114 }
115
116 for (int i = 0; i < dexFiles.size(); i++) {
117 jclass bootHelperClass = env->FindClass(kAndroidAgentClass.c_str());
118 if (bootHelperClass == nullptr) {
119 std::cerr << kAndroidAgentClass << " could not be found" << std::endl;
120 exit(1);
121 }
122
123 jmethodID getBytecodeFromDex =
124 env->GetStaticMethodID(bootHelperClass, "getBytecodeFromDex",
125 "(Ljava/lang/String;Ljava/lang/String;)[B");
126 if (getBytecodeFromDex == nullptr) {
127 std::cerr << "'getBytecodeFromDex' not found\n";
128 exit(1);
129 }
130
131 jstring jjarPath = env->NewStringUTF(jarPath.data());
132 jstring jdexFile = env->NewStringUTF(dexFiles[i].data());
133
134 int length = 1;
135 std::vector<unsigned char> dexFileBytes;
136
137 jbyteArray dexBytes = (jbyteArray)env->CallStaticObjectMethod(
138 bootHelperClass, getBytecodeFromDex, jjarPath, jdexFile);
139
140 if (env->ExceptionCheck()) {
141 env->ExceptionDescribe();
142 exit(1);
143 }
144
145 jbyte* data = new jbyte;
146 data = env->GetByteArrayElements(dexBytes, 0);
147 length = env->GetArrayLength(dexBytes);
148
149 for (int j = 0; j < length; j++) {
150 dexFileBytes.push_back(data[j]);
151 }
152
153 env->DeleteLocalRef(dexBytes);
154 env->DeleteLocalRef(jjarPath);
155 env->DeleteLocalRef(jdexFile);
156 env->DeleteLocalRef(bootHelperClass);
157
158 unsigned char* usData = reinterpret_cast<unsigned char*>(&dexFileBytes[0]);
159 dfm.addDexFile(usData, length);
160 }
161 }
162
jazzerClassFileLoadHook(jvmtiEnv * jvmti,JNIEnv * jni_env,jclass class_being_redefined,jobject loader,const char * name,jobject protection_domain,jint class_data_len,const unsigned char * class_data,jint * new_class_data_len,unsigned char ** new_class_data)163 void JNICALL jazzerClassFileLoadHook(
164 jvmtiEnv* jvmti, JNIEnv* jni_env, jclass class_being_redefined,
165 jobject loader, const char* name, jobject protection_domain,
166 jint class_data_len, const unsigned char* class_data,
167 jint* new_class_data_len, unsigned char** new_class_data) {
168 // check if Jazzer class
169 const char* prefix = "com/code_intelligence/jazzer/";
170 if (strncmp(name, prefix, 29) == 0) {
171 return;
172 }
173
174 int indx = dfm.findDexFileForClass(name);
175 if (indx < 0) {
176 return;
177 }
178
179 size_t newSize;
180 unsigned char* newClassDataResult =
181 dfm.getClassBytes(name, indx, jvmti, &newSize);
182
183 dex::Reader oldReader(const_cast<unsigned char*>(class_data),
184 (size_t)class_data_len);
185 dex::Reader newReader(newClassDataResult, newSize);
186 if (dfm.structureMatches(&oldReader, &newReader, name)) {
187 std::cout << "REDEFINING WITH INSTRUMENTATION: " << name << std::endl;
188 *new_class_data = newClassDataResult;
189 *new_class_data_len = static_cast<jint>(newSize);
190 }
191 }
192
fileExists(std::string filePath)193 bool fileExists(std::string filePath) { return std::ifstream(filePath).good(); }
194
jazzerVMInit(jvmtiEnv * jvmti_env,JNIEnv * jni_env,jthread thread)195 void JNICALL jazzerVMInit(jvmtiEnv* jvmti_env, JNIEnv* jni_env,
196 jthread thread) {
197 // Parse agentOptions
198
199 std::stringstream ss(agentOptions);
200 std::string token;
201
202 std::string jazzerClassesJar;
203 std::vector<std::string> bootpathClassesOverrides;
204 while (std::getline(ss, token, ',')) {
205 std::vector<std::string> split =
206 absl::StrSplit(token, absl::MaxSplits('=', 1));
207 if (split.size() < 2) {
208 std::cerr << "ERROR: no option given for: " << token;
209 exit(1);
210 }
211
212 if (split[0] == "injectJars") {
213 jazzerClassesJar = split[1];
214 } else if (split[0] == "bootstrapClassOverrides") {
215 bootpathClassesOverrides =
216 absl::StrSplit(split[1], absl::MaxSplits(':', 10));
217 }
218 }
219
220 if (!fileExists(jazzerClassesJar)) {
221 std::cerr << "ERROR: Jazzer bootstrap class file not found at: "
222 << jazzerClassesJar << std::endl;
223 exit(1);
224 }
225
226 jvmti_env->AddToBootstrapClassLoaderSearch(jazzerClassesJar.c_str());
227
228 jvmtiCapabilities jazzerJvmtiCapabilities = {
229 .can_tag_objects = 0,
230 .can_generate_field_modification_events = 0,
231 .can_generate_field_access_events = 0,
232 .can_get_bytecodes = 0,
233 .can_get_synthetic_attribute = 0,
234 .can_get_owned_monitor_info = 0,
235 .can_get_current_contended_monitor = 0,
236 .can_get_monitor_info = 0,
237 .can_pop_frame = 0,
238 .can_redefine_classes = 1,
239 .can_signal_thread = 0,
240 .can_get_source_file_name = 1,
241 .can_get_line_numbers = 0,
242 .can_get_source_debug_extension = 0,
243 .can_access_local_variables = 0,
244 .can_maintain_original_method_order = 0,
245 .can_generate_single_step_events = 0,
246 .can_generate_exception_events = 0,
247 .can_generate_frame_pop_events = 0,
248 .can_generate_breakpoint_events = 0,
249 .can_suspend = 0,
250 .can_redefine_any_class = 0,
251 .can_get_current_thread_cpu_time = 0,
252 .can_get_thread_cpu_time = 0,
253 .can_generate_method_entry_events = 0,
254 .can_generate_method_exit_events = 0,
255 .can_generate_all_class_hook_events = 0,
256 .can_generate_compiled_method_load_events = 0,
257 .can_generate_monitor_events = 0,
258 .can_generate_vm_object_alloc_events = 0,
259 .can_generate_native_method_bind_events = 0,
260 .can_generate_garbage_collection_events = 0,
261 .can_generate_object_free_events = 0,
262 .can_force_early_return = 0,
263 .can_get_owned_monitor_stack_depth_info = 0,
264 .can_get_constant_pool = 0,
265 .can_set_native_method_prefix = 0,
266 .can_retransform_classes = 1,
267 .can_retransform_any_class = 0,
268 .can_generate_resource_exhaustion_heap_events = 0,
269 .can_generate_resource_exhaustion_threads_events = 0,
270 };
271
272 jvmtiError je = jvmti_env->AddCapabilities(&jazzerJvmtiCapabilities);
273 if (je != JVMTI_ERROR_NONE) {
274 std::cerr << "JVMTI ERROR: " << je << std::endl;
275 exit(1);
276 }
277
278 for (int i = 0; i < bootpathClassesOverrides.size(); i++) {
279 if (!fileExists(bootpathClassesOverrides[i])) {
280 std::cerr << "ERROR: Bootpath Class override jar not found at: "
281 << bootpathClassesOverrides[i] << std::endl;
282 exit(1);
283 }
284
285 initializeBootclassOverrideJar(bootpathClassesOverrides[i], jni_env);
286 }
287
288 retransformLoadedClasses(jvmti_env, jni_env);
289 }
290
Agent_OnLoad(JavaVM * vm,char * options,void * reserved)291 JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
292 jvmtiEnv* jvmti = nullptr;
293 if (vm->GetEnv((void**)&jvmti, JVMTI_VERSION_1_2) != JNI_OK) {
294 return 1;
295 }
296
297 jvmtiEventCallbacks callbacks;
298
299 memset(&callbacks, 0, sizeof(callbacks));
300 callbacks.ClassFileLoadHook = jazzerClassFileLoadHook;
301 callbacks.VMInit = jazzerVMInit;
302
303 jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks));
304 jvmti->SetEventNotificationMode(JVMTI_ENABLE,
305 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);
306 jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);
307
308 // Save the options string here, this is the only time it will be available
309 // however, we wont be able to use this to initialize until VMInit callback is
310 // called
311 agentOptions = std::string(options);
312 return 0;
313 }
314