Skip to content

ghidra: fix invalid address expection and handle partial union #81

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 16, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions lib/patchestry/Ghidra/JsonDeserialize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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;
}
}

Expand Down
52 changes: 45 additions & 7 deletions scripts/ghidra/PatchestryDecompileFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -846,7 +851,20 @@ private Address convertAddressToRamSpace(Address address) throws Exception {
if (address == null) {
return null;
}
return ram_space.getAddress(address.toString(false));

try {
// If already in RAM space, just return it
if (address.getAddressSpace().getName().equals("ram")) {
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 (Exception e) {
println(String.format("Failed to convert address %s to RAM space: %s", address, e.getMessage()));
return null;
}
}

private boolean isCharPointer(Varnode node) throws Exception {
Expand Down Expand Up @@ -875,6 +893,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());
Expand All @@ -891,14 +912,31 @@ 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.
Address ram_address = convertAddressToRamSpace(node.getAddress());
Data data = getDataAt(ram_address);
return data;
try {
// Convert the constant value to a potential RAM address
Address ram_address = convertAddressToRamSpace(node.getAddress());
if (ram_address == null) {
return null;
}
return getDataAt(ram_address);

} catch (AddressOutOfBoundsException e) {
println("Address conversion out of bounds for constant: " + e.getMessage());
}

return null;
}

// Serialize an input or output varnode.
Expand Down