fix ghidra script failing if strings overlapped (for example, load_min_version and version could point to the same region of memory)

develop
Ben Lubar 2020-03-08 13:54:58 -05:00
parent a7d263fa67
commit 923581b144
No known key found for this signature in database
GPG Key ID: 92939677AB59EDA4
2 changed files with 38 additions and 17 deletions

@ -173,8 +173,8 @@ private:
const std::string *check_possible_stl_string_pointer(const void *const*); const std::string *check_possible_stl_string_pointer(const void *const*);
#endif #endif
bool check_access(const ToCheck &, void *, type_identity *); bool check_access(const ToCheck &, void *, type_identity *);
bool check_access(const ToCheck &, void *, type_identity *, size_t); bool check_access(const ToCheck &, void *, type_identity *, size_t, bool = false);
const char *check_vtable(const ToCheck &, void *, type_identity *); const char *check_vtable(const ToCheck &, void *, type_identity *, bool = false);
void queue_field(ToCheck &&, const struct_field_info *); void queue_field(ToCheck &&, const struct_field_info *);
void queue_static_array(const ToCheck &, void *, type_identity *, size_t, bool = false, enum_identity * = nullptr); void queue_static_array(const ToCheck &, void *, type_identity *, size_t, bool = false, enum_identity * = nullptr);
bool maybe_queue_union(const ToCheck &, const struct_field_info *, const struct_field_info *); bool maybe_queue_union(const ToCheck &, const struct_field_info *, const struct_field_info *);
@ -427,7 +427,7 @@ bool Checker::check_access(const ToCheck & item, void *base, type_identity *iden
return check_access(item, base, identity, identity ? identity->byte_size() : 0); return check_access(item, base, identity, identity ? identity->byte_size() : 0);
} }
bool Checker::check_access(const ToCheck & item, void *base, type_identity *identity, size_t size) bool Checker::check_access(const ToCheck & item, void *base, type_identity *identity, size_t size, bool quiet)
{ {
if (!base) if (!base)
{ {
@ -445,7 +445,10 @@ bool Checker::check_access(const ToCheck & item, void *base, type_identity *iden
#endif #endif
if (reinterpret_cast<uintptr_t>(base) == UNINIT_PTR) if (reinterpret_cast<uintptr_t>(base) == UNINIT_PTR)
{ {
FAIL_PTR("uninitialized pointer"); if (!quiet)
{
FAIL_PTR("uninitialized pointer");
}
return false; return false;
} }
@ -467,7 +470,10 @@ bool Checker::check_access(const ToCheck & item, void *base, type_identity *iden
if (!range.valid || !range.read) if (!range.valid || !range.read)
{ {
FAIL_PTR("pointer to invalid memory range"); if (!quiet)
{
FAIL_PTR("pointer to invalid memory range");
}
return false; return false;
} }
@ -483,6 +489,11 @@ bool Checker::check_access(const ToCheck & item, void *base, type_identity *iden
} }
} }
if (quiet)
{
return false;
}
if (expected_start == base) if (expected_start == base)
{ {
FAIL_PTR("pointer not in any mapped range"); FAIL_PTR("pointer not in any mapped range");
@ -495,14 +506,14 @@ bool Checker::check_access(const ToCheck & item, void *base, type_identity *iden
#undef FAIL_PTR #undef FAIL_PTR
} }
const char *Checker::check_vtable(const ToCheck & item, void *vtable, type_identity *identity) const char *Checker::check_vtable(const ToCheck & item, void *vtable, type_identity *identity, bool quiet)
{ {
if (!check_access(item, PTR_ADD(vtable, -ptrdiff_t(sizeof(void *))), identity, sizeof(void *))) if (!check_access(item, PTR_ADD(vtable, -ptrdiff_t(sizeof(void *))), identity, sizeof(void *), quiet))
return nullptr; return nullptr;
char **info = *(reinterpret_cast<char ***>(vtable) - 1); char **info = *(reinterpret_cast<char ***>(vtable) - 1);
#ifdef WIN32 #ifdef WIN32
if (!check_access(item, PTR_ADD(info, 12), identity, 4)) if (!check_access(item, PTR_ADD(info, 12), identity, 4, quiet))
return nullptr; return nullptr;
#ifdef DFHACK64 #ifdef DFHACK64
@ -516,7 +527,7 @@ const char *Checker::check_vtable(const ToCheck & item, void *vtable, type_ident
char *name = reinterpret_cast<char *>(info) + 8; char *name = reinterpret_cast<char *>(info) + 8;
#endif #endif
#else #else
if (!check_access(item, info + 1, identity, sizeof(void *))) if (!check_access(item, info + 1, identity, sizeof(void *), quiet))
return nullptr; return nullptr;
char *name = *(info + 1); char *name = *(info + 1);
#endif #endif
@ -530,7 +541,10 @@ const char *Checker::check_vtable(const ToCheck & item, void *vtable, type_ident
if (!range.valid || !range.read) if (!range.valid || !range.read)
{ {
FAIL("pointer to invalid memory range"); if (!quiet)
{
FAIL("pointer to invalid memory range");
}
return nullptr; return nullptr;
} }
@ -863,18 +877,18 @@ void Checker::check_dispatch(ToCheck & item)
size_t allocated_size = *reinterpret_cast<size_t *>(PTR_ADD(item.ptr, -16)); size_t allocated_size = *reinterpret_cast<size_t *>(PTR_ADD(item.ptr, -16));
FAIL("pointer to a block of " << allocated_size << " bytes of allocated memory"); FAIL("pointer to a block of " << allocated_size << " bytes of allocated memory");
if (allocated_size >= MIN_SIZE_FOR_SUGGEST && known_types_by_size.count(allocated_size))
{
FAIL("known types of this size: " << join_strings(", ", known_types_by_size.at(allocated_size)));
}
// check recursively if it might be a valid pointer // check recursively if it's the right size for a pointer or if it starts with what might be a valid pointer
if (allocated_size == sizeof(void *)) if (allocated_size == sizeof(void *) || (allocated_size > sizeof(void *) && check_access(item, item.ptr, df::identity_traits<void *>::get(), sizeof(void *), true)))
{ {
item.path.push_back(".?ptr?"); item.path.push_back(".?ptr?");
item.path.push_back(""); item.path.push_back("");
item.identity = df::identity_traits<void *>::get(); item.identity = df::identity_traits<void *>::get();
} }
else if (allocated_size >= MIN_SIZE_FOR_SUGGEST && known_types_by_size.count(allocated_size))
{
FAIL("known types of this size: " << join_strings(", ", known_types_by_size.at(allocated_size)));
}
} }
#ifndef WIN32 #ifndef WIN32
else if (auto str = check_possible_stl_string_pointer(&item.ptr)) else if (auto str = check_possible_stl_string_pointer(&item.ptr))
@ -907,6 +921,8 @@ void Checker::check_dispatch(ToCheck & item)
// special case for large_integer weirdness // special case for large_integer weirdness
if (item.identity == df::identity_traits<df::large_integer>::get()) if (item.identity == df::identity_traits<df::large_integer>::get())
{ {
// it's 16 bytes on 64-bit linux due to a messy header in libgraphics
// but only the first 8 bytes are ever used
item.identity = df::identity_traits<int64_t>::get(); item.identity = df::identity_traits<int64_t>::get();
} }
@ -1433,6 +1449,11 @@ void Checker::check_struct(const ToCheck & item)
if (allocated_size != expected_size) if (allocated_size != expected_size)
{ {
FAIL("allocated structure size (" << allocated_size << ") does not match expected size (" << expected_size << ")"); FAIL("allocated structure size (" << allocated_size << ") does not match expected size (" << expected_size << ")");
if (allocated_size >= MIN_SIZE_FOR_SUGGEST && known_types_by_size.count(allocated_size))
{
FAIL("known types of this size: " << join_strings(", ", known_types_by_size.at(allocated_size)));
}
} }
} }
else else

@ -61,7 +61,7 @@ public class find_df_globals extends GhidraScript {
dataAddr = globalAddr.getNewAddress(mem.getInt(globalAddr.add(globalCount * ptrSize * 2 + ptrSize))); dataAddr = globalAddr.getNewAddress(mem.getInt(globalAddr.add(globalCount * ptrSize * 2 + ptrSize)));
} }
String name = StringDataInstance.getStringDataInstance(currentProgram.getListing().createData(nameAddr, TerminatedStringDataType.dataType)).getStringValue(); String name = StringDataInstance.getStringDataInstance(DataUtilities.createData(currentProgram, nameAddr, TerminatedStringDataType.dataType, 0, false, DataUtilities.ClearDataMode.CLEAR_ALL_CONFLICT_DATA)).getStringValue();
createLabel(dataAddr, name, true); createLabel(dataAddr, name, true);
} }