diff options
Diffstat (limited to 'src/main/native/com/code_intelligence/jazzer/android/dex_file_manager.cpp')
-rw-r--r-- | src/main/native/com/code_intelligence/jazzer/android/dex_file_manager.cpp | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/src/main/native/com/code_intelligence/jazzer/android/dex_file_manager.cpp b/src/main/native/com/code_intelligence/jazzer/android/dex_file_manager.cpp new file mode 100644 index 00000000..b409e82b --- /dev/null +++ b/src/main/native/com/code_intelligence/jazzer/android/dex_file_manager.cpp @@ -0,0 +1,208 @@ +// Copyright 2023 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dex_file_manager.h" + +#include <algorithm> +#include <iostream> +#include <sstream> +#include <string> +#include <vector> + +#include "jazzer_jvmti_allocator.h" +#include "jvmti.h" +#include "slicer/dex_ir.h" +#include "slicer/reader.h" +#include "slicer/writer.h" + +std::string GetName(const char* name) { + std::stringstream ss; + // Class name needs to be in the format "L<class_name>;" as it is stored in + // the types table in the DEX file for slicer to find it + ss << "L" << name << ";"; + return ss.str(); +} + +bool IsValidIndex(dex::u4 index) { return index != (unsigned)-1; } + +void DexFileManager::addDexFile(const unsigned char* bytes, int length) { + unsigned char* newArr = new unsigned char[length]; + std::copy(bytes, bytes + length, newArr); + + dexFiles.push_back(newArr); + dexFilesSize.push_back(length); +} + +unsigned char* DexFileManager::getClassBytes(const char* className, + int dexFileIndex, jvmtiEnv* jvmti, + size_t* newSize) { + dex::Reader dexReader(dexFiles[dexFileIndex], dexFilesSize[dexFileIndex]); + auto descName = GetName(className); + + auto classIndex = dexReader.FindClassIndex(descName.c_str()); + if (!IsValidIndex(classIndex)) { + *newSize = *newSize; + return nullptr; + } + + dexReader.CreateClassIr(classIndex); + auto oldIr = dexReader.GetIr(); + + dex::Writer writer(oldIr); + JazzerJvmtiAllocator allocator(jvmti); + return writer.CreateImage(&allocator, newSize); +} + +uint32_t DexFileManager::findDexFileForClass(const char* className) { + for (int i = 0; i < dexFiles.size(); i++) { + dex::Reader dexReader(dexFiles[i], dexFilesSize[i]); + + std::string descName = GetName(className); + dex::u4 classIndex = dexReader.FindClassIndex(descName.c_str()); + + if (IsValidIndex(classIndex)) { + return i; + } + } + + return -1; +} + +std::vector<std::string> getMethodDescriptions( + std::vector<ir::EncodedMethod*>* encMethodList) { + std::vector<std::string> methodDescs; + + for (int i = 0; i < encMethodList->size(); i++) { + std::stringstream ss; + ss << (*encMethodList)[i]->access_flags; + ss << (*encMethodList)[i]->decl->name->c_str(); + ss << (*encMethodList)[i]->decl->prototype->Signature().c_str(); + + methodDescs.push_back(ss.str()); + } + + sort(methodDescs.begin(), methodDescs.end()); + return methodDescs; +} + +std::vector<std::string> getFieldDescriptions( + std::vector<ir::EncodedField*>* encFieldList) { + std::vector<std::string> fieldDescs; + + for (int i = 0; i < encFieldList->size(); i++) { + std::stringstream ss; + ss << (*encFieldList)[i]->access_flags; + ss << (*encFieldList)[i]->decl->type->descriptor->c_str(); + ss << (*encFieldList)[i]->decl->name->c_str(); + fieldDescs.push_back(ss.str()); + } + + sort(fieldDescs.begin(), fieldDescs.end()); + return fieldDescs; +} + +bool matchFields(std::vector<ir::EncodedField*>* encodedFieldListOne, + std::vector<ir::EncodedField*>* encodedFieldListTwo) { + std::vector<std::string> fDescListOne = + getFieldDescriptions(encodedFieldListOne); + std::vector<std::string> fDescListTwo = + getFieldDescriptions(encodedFieldListTwo); + + if (fDescListOne.size() != fDescListTwo.size()) { + return false; + } + + for (int i = 0; i < fDescListOne.size(); i++) { + if (fDescListOne[i] != fDescListTwo[i]) { + return false; + } + } + + return true; +} + +bool matchMethods(std::vector<ir::EncodedMethod*>* encodedMethodListOne, + std::vector<ir::EncodedMethod*>* encodedMethodListTwo) { + std::vector<std::string> mDescListOne = + getMethodDescriptions(encodedMethodListOne); + std::vector<std::string> mDescListTwo = + getMethodDescriptions(encodedMethodListTwo); + + if (mDescListOne.size() != mDescListTwo.size()) { + return false; + } + + for (int i = 0; i < mDescListOne.size(); i++) { + if (mDescListOne[i] != mDescListTwo[i]) { + return false; + } + } + + return true; +} + +bool classStructureMatches(ir::Class* classOne, ir::Class* classTwo) { + return matchMethods(&(classOne->direct_methods), + &(classTwo->direct_methods)) && + matchMethods(&(classOne->virtual_methods), + &(classTwo->virtual_methods)) && + matchFields(&(classOne->static_fields), &(classTwo->static_fields)) && + matchFields(&(classOne->instance_fields), + &(classTwo->instance_fields)) && + classOne->access_flags == classTwo->access_flags; +} + +bool DexFileManager::structureMatches(dex::Reader* oldReader, + dex::Reader* newReader, + const char* className) { + std::string descName = GetName(className); + + dex::u4 oldReaderIndex = oldReader->FindClassIndex(descName.c_str()); + dex::u4 newReaderIndex = newReader->FindClassIndex(descName.c_str()); + + if (!IsValidIndex(oldReaderIndex) || !IsValidIndex(newReaderIndex)) { + return false; + } + + oldReader->CreateClassIr(oldReaderIndex); + newReader->CreateClassIr(newReaderIndex); + + std::shared_ptr<ir::DexFile> oldDexFile = oldReader->GetIr(); + std::shared_ptr<ir::DexFile> newDexFile = newReader->GetIr(); + + for (int i = 0; i < oldDexFile->classes.size(); i++) { + const char* oldClassDescriptor = + oldDexFile->classes[i]->type->descriptor->c_str(); + if (strcmp(oldClassDescriptor, descName.c_str()) != 0) { + continue; + } + + bool match = false; + for (int j = 0; j < newDexFile->classes.size(); j++) { + const char* newClassDescriptor = + newDexFile->classes[j]->type->descriptor->c_str(); + if (strcmp(oldClassDescriptor, newClassDescriptor) == 0) { + match = classStructureMatches(oldDexFile->classes[i].get(), + newDexFile->classes[j].get()); + break; + } + } + + if (!match) { + return false; + } + } + + return true; +} |