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