diff --git a/lib/patchestry/Ghidra/JsonDeserialize.cpp b/lib/patchestry/Ghidra/JsonDeserialize.cpp index 167bb9fa..e2342d4d 100644 --- a/lib/patchestry/Ghidra/JsonDeserialize.cpp +++ b/lib/patchestry/Ghidra/JsonDeserialize.cpp @@ -199,7 +199,7 @@ namespace patchestry::ghidra { auto size = static_cast< uint32_t >(type_obj.getInteger("size").value_or(0)); auto kind_str = get_string(type_obj, "kind"); LOG(INFO) << "Attempting to convert kind string: [" << kind_str << "]" << "\n"; - auto kind = VarnodeType::convertToKind(kind_str); + auto kind = VarnodeType::convertToKind(kind_str); switch (kind) { case VarnodeType::Kind::VT_INVALID: { LOG(ERROR) << "Invalid varnode type: " << name << "\n"; @@ -324,40 +324,56 @@ namespace patchestry::ghidra { } // Iterate through the fields and initialize them + unsigned field_index = 0; for (const auto &field : *field_array) { const auto *field_obj = field.getAsObject(); if (field_obj == nullptr) { - LOG(ERROR) << "Skipping invalid field object.\n"; + LOG(ERROR) << "Field #" << field_index + << ": Invalid field object format, skipping\n"; + ++field_index; continue; } auto field_type_key = get_string_if_valid(*field_obj, "type"); if (!field_type_key) { - LOG(ERROR) << "Skipping field: missing type\n"; + LOG(ERROR) << "Field #" << field_index + << ": Missing required 'type' attribute, skipping\n"; + ++field_index; continue; } auto iter = serialized_types.find(*field_type_key); if (iter == serialized_types.end()) { - LOG(ERROR) << "Skipping field: component is not found on serialized types.\n"; + LOG(ERROR) << "Field #" << field_index << ": Type '" << *field_type_key + << "' not found in serialized types registry, skipping\n"; + ++field_index; continue; } auto maybe_offset = field_obj->getInteger("offset"); if (!maybe_offset) { - LOG(ERROR) << "Skipping field: invalid offset value.\n"; + LOG(ERROR) << "Field #" << field_index + << ": Missing or invalid 'offset' value, skipping\n"; + ++field_index; continue; } + std::string field_name; auto maybe_name = get_string_if_valid(*field_obj, "name"); - if (!maybe_name) { - LOG(ERROR) << "Skipping field: invalid name.\n"; - continue; + if (maybe_name) { + field_name = *maybe_name; + } else { + field_name = "field_" + std::to_string(field_index); + LOG(WARNING) << "Field #" << field_index + << " Missing or invalid name, using default: '" << field_name + << "'\n"; } varnode.add_components( - *maybe_name, *iter->second, static_cast< uint32_t >(*maybe_offset) + field_name, *iter->second, static_cast< uint32_t >(*maybe_offset) ); + + ++field_index; } } diff --git a/scripts/ghidra/PatchestryDecompileFunctions.java b/scripts/ghidra/PatchestryDecompileFunctions.java index 7fafc882..fa20fbee 100644 --- a/scripts/ghidra/PatchestryDecompileFunctions.java +++ b/scripts/ghidra/PatchestryDecompileFunctions.java @@ -27,6 +27,7 @@ import ghidra.program.model.address.AddressSet; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.address.AddressOutOfBoundsException; import ghidra.program.model.block.BasicBlockModel; import ghidra.program.model.block.CodeBlock; @@ -576,9 +577,13 @@ private void serialize(DataType data_type) throws Exception { serializePrototype((FunctionSignature) data_type); } else if (data_type instanceof PartialUnion) { - name("kind").value("todo:PartialUnion"); // TODO(pag): Implement this - name("size").value(data_type.getLength()); - + DataType parent = ((PartialUnion) data_type).getParent(); + if (parent != data_type) { + serialize(parent); + } else { + // PartialUnion stripped type is undefined type + serialize(((PartialUnion) data_type).getStrippedDataType()); + } } else if (data_type instanceof BitFieldDataType) { name("kind").value("todo:BitFieldDataType"); // TODO(pag): Implement this name("size").value(data_type.getLength()); @@ -846,7 +851,25 @@ private Address convertAddressToRamSpace(Address address) throws Exception { if (address == null) { return null; } - return ram_space.getAddress(address.toString(false)); + + try { + // Note: Function converts address to ramspace only if it belongs to + // constant space; if address space is not constant, return + if (!address.getAddressSpace().isConstantSpace()) { + return address; + } + + // Get the numeric offset and create a new address in RAM space + long offset = address.getOffset(); + return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset); + + } catch (AddressOutOfBoundsException e) { + println(String.format("Error converting address %s to RAM space: %s", + address, e.getMessage())); + return null; + } catch (Exception e) { + throw new RuntimeException("Failed converting address to RAM space", e); + } } private boolean isCharPointer(Varnode node) throws Exception { @@ -875,6 +898,9 @@ private String findNullTerminatedString(Address address, Pointer pointer) throws } Address ram_address = convertAddressToRamSpace(address); + if (ram_address == null) { + return null; + } MemoryBufferImpl memoryBuffer = new MemoryBufferImpl(program.getMemory(), ram_address); DataType char_type = pointer.getDataType(); //println("Debug: char_type = " + char_type.getName() + ", size = " + char_type.getLength()); @@ -891,14 +917,24 @@ private String findNullTerminatedString(Address address, Pointer pointer) throws } private Data getDataReferencedAsConstant(Varnode node) throws Exception { + // check if node is null + if (node == null) { + return null; + } + + // Only process constant nodes that aren't nulls (address 0 in constant space) if (!node.isConstant() || node.getAddress().equals(constant_space.getAddress(0))) { return null; } + // Ghidra sometime fail to resolve references to Data and show it as const. // Check if it is referencing Data as constant from `ram` addresspace. + // Convert the constant value to a potential RAM address Address ram_address = convertAddressToRamSpace(node.getAddress()); - Data data = getDataAt(ram_address); - return data; + if (ram_address == null) { + return null; + } + return getDataAt(ram_address); } // Serialize an input or output varnode.