diff --git a/ida/digest.cc b/ida/digest.cc index fa16dfc8..ae4eb2f2 100644 --- a/ida/digest.cc +++ b/ida/digest.cc @@ -18,6 +18,7 @@ #include "third_party/zynamics/binexport/ida/begin_idasdk.inc" // NOLINT #include // NOLINT #include // NOLINT +#include // NOLINT #include "third_party/zynamics/binexport/ida/end_idasdk.inc" // NOLINT // clang-format on @@ -32,8 +33,12 @@ absl::StatusOr GetInputFileSha256() { std::string hash(kNumSha256Bytes, '\0'); auto* hash_uchar = reinterpret_cast(&hash[0]); if (!retrieve_input_file_sha256(hash_uchar) && +#if IDP_INTERFACE_VERSION >= 900 + (netnode_supval(0, RIDX_SHA256, hash_uchar, kNumSha256Bytes, 'S') != +#else // b/186782665: IDA 7.5 and lower use the root_node instead. (root_node.supval(RIDX_SHA256, hash_uchar, kNumSha256Bytes) != +#endif kNumSha256Bytes)) { return absl::InternalError("Failed to load SHA256 hash of input file"); } @@ -45,8 +50,13 @@ absl::StatusOr GetInputFileMd5() { std::string hash(kNumMd5Bytes, '\0'); auto* hash_uchar = reinterpret_cast(&hash[0]); if (!retrieve_input_file_md5(hash_uchar) && +#if IDP_INTERFACE_VERSION >= 900 + (netnode_supval(0, RIDX_MD5, hash_uchar, kNumMd5Bytes, 'S') != + kNumMd5Bytes)) { +#else // b/186782665: IDA 7.5 and lower use the root_node instead. (root_node.supval(RIDX_MD5, hash_uchar, kNumMd5Bytes) != kNumMd5Bytes)) { +#endif return absl::InternalError("Failed to load MD5 hash of input file"); } return absl::AsciiStrToLower(absl::BytesToHexString(hash)); diff --git a/ida/flow_analysis.cc b/ida/flow_analysis.cc index c96febe2..7b696ea8 100644 --- a/ida/flow_analysis.cc +++ b/ida/flow_analysis.cc @@ -18,15 +18,19 @@ #include "third_party/zynamics/binexport/ida/begin_idasdk.inc" // NOLINT #include // NOLINT #include // NOLINT -#include // NOLINT #include // NOLINT #include // NOLINT #include // NOLINT #include // NOLINT #include // NOLINT -#include // NOLINT #include // NOLINT #include // NOLINT +#if IDP_INTERFACE_VERSION >= 900 +#define ph PH +#else +#include // NOLINT +#include // NOLINT +#endif #include "third_party/zynamics/binexport/ida/end_idasdk.inc" // NOLINT // clang-format on diff --git a/ida/main_plugin.cc b/ida/main_plugin.cc index 1b201b11..7bc9ca56 100644 --- a/ida/main_plugin.cc +++ b/ida/main_plugin.cc @@ -26,6 +26,9 @@ #include // NOLINT #include // NOLINT #include // NOLINT +#if IDP_INTERFACE_VERSION >= 900 +#define ph PH +#endif #include "third_party/zynamics/binexport/ida/end_idasdk.inc" // NOLINT // clang-format on diff --git a/ida/names.cc b/ida/names.cc index d7eb86da..468cc534 100644 --- a/ida/names.cc +++ b/ida/names.cc @@ -14,6 +14,7 @@ #include "third_party/zynamics/binexport/ida/names.h" +#include #include #include #include @@ -26,7 +27,6 @@ #include "third_party/zynamics/binexport/ida/begin_idasdk.inc" // NOLINT #include // NOLINT #include // NOLINT -#include // NOLINT #include // NOLINT #include // NOLINT #include // NOLINT @@ -34,9 +34,14 @@ #include // NOLINT #include // NOLINT #include // NOLINT -#include // NOLINT #include // NOLINT #include // NOLINT +#if IDP_INTERFACE_VERSION >= 900 +#define ph PH +#else +#include // NOLINT +#include // NOLINT +#endif #include "third_party/zynamics/binexport/ida/end_idasdk.inc" // NOLINT // clang-format on @@ -136,8 +141,12 @@ absl::optional GetArchitectureName() { std::string GetModuleName() { std::string path(QMAXPATH, '\0'); if (get_input_file_path(&path[0], QMAXPATH) == 0) { +#if IDP_INTERFACE_VERSION >= 900 + netnode_valstr(0, &path[0], QMAXPATH); +#else // b/186782665: IDA 7.5 and lower use the root_node instead. root_node.valstr(&path[0], QMAXPATH); +#endif } path.resize(std::strlen(path.data())); return Basename(path); @@ -274,6 +283,62 @@ std::string GetVariableName(const insn_t& instruction, uint8_t operand_num) { return ""; } +#if IDP_INTERFACE_VERSION >= 900 + func_t* function = get_func(instruction.ea); + if (!function) { + return ""; + } + + tinfo_t frame; + if (!get_func_frame(&frame, function)) { + return ""; + } + + ea_t offset = calc_stkvar_struc_offset(function, instruction, operand_num); + + udt_type_data_t udt_data; + if (!frame.get_udt_details(&udt_data)) { + return ""; + } + // Find the base member (variable or structure) + const udm_t* base_member = nullptr; + for (const auto& member : udt_data) { + if (member.offset / 8 <= offset && + (member.offset / 8 + member.size / 8) > offset) { + base_member = &member; + break; + } + } + + if (base_member) { + std::string result = ToString(base_member->name); + + if (ea_t member_offset = offset - (base_member->offset / 8); + member_offset > 0) { + // Handle nested structures + if (base_member->type.is_struct()) { + udt_type_data_t nested_udt_data; + if (base_member->type.get_udt_details(&nested_udt_data)) { + for (const auto& nested_member : nested_udt_data) { + if (nested_member.offset / 8 == member_offset) { + return absl::StrCat(result, ".", ToString(nested_member.name)); + } + } + } + } + + // If it's not a nested structure or we couldn't find the exact member, + // append the offset + absl::StrAppend(&result, "+", IdaHexify(member_offset)); + } + + return result; + } + + // If we couldn't find a matching member, return a generic name with the + // offset + return absl::StrCat("var_", IdaHexify(offset)); +#else const member_t* stack_variable = get_stkvar(0, instruction, instruction.ops[operand_num], instruction.ops[operand_num].addr); @@ -338,17 +403,50 @@ std::string GetVariableName(const insn_t& instruction, uint8_t operand_num) { return result; } return ""; +#endif } std::string GetGlobalStructureName(Address address, Address instance_address, uint8_t operand_num) { std::string instance_name; + tid_t id[MAXSTRUCPATH]; memset(id, 0, sizeof(id)); adiff_t disp = 0; adiff_t delta = 0; int num_structs = get_struct_operand(&disp, &delta, id, address, operand_num); +#if IDP_INTERFACE_VERSION >= 900 + if (num_structs > 0) { + tinfo_t tif; + if (get_tinfo(&tif, id[0])) { + qstring ida_name; + if (get_name(&ida_name, instance_address - disp) || + tif.get_type_name(&ida_name)) { + instance_name = ToString(ida_name); + } + + udt_type_data_t udt_data; + if (tif.get_udt_details(&udt_data)) { + for (const udm_t& udm : udt_data) { + if (udm.offset < disp) { + continue; + } + absl::StrAppend(&instance_name, ".", ToString(udm.name)); + disp -= udm.size; + + if (!udm.type.is_struct()) { + break; + } + tif = udm.type; + if (!tif.get_udt_details(&udt_data)) { + break; + } + } + } + } + } +#else if (num_structs > 0) { // Special case for the first index - this may be an instance name instead // of a type name. @@ -356,9 +454,8 @@ std::string GetGlobalStructureName(Address address, Address instance_address, if (structure) { // First try to get a global variable instance name. // Second, fall back to just the structure type name. - qstring ida_name; - if (get_name(&ida_name, instance_address - disp) || - get_struc_name(&ida_name, id[0])) { + if (qstring ida_name; get_name(&ida_name, instance_address - disp) || + get_struc_name(&ida_name, id[0])) { instance_name = ToString(ida_name); } } @@ -373,6 +470,7 @@ std::string GetGlobalStructureName(Address address, Address instance_address, structure = get_sptr(member); } } +#endif return instance_name; } @@ -471,13 +569,26 @@ void GetRegularComments(Address address, Comments* comments) { void GetEnumComments(Address address, Comments* comments) { // @bug: there is an get_enum_cmt // function in IDA as well! +#if IDP_INTERFACE_VERSION >= 900 + if (is_enum0(get_flags(address)) || is_enum1(get_flags(address))) { + tinfo_t tif; + if (get_tinfo(&tif, address) && tif.is_enum()) { + qstring enum_name; + if (tif.get_type_name(&enum_name)) { + comments->emplace_back(address, is_enum0(get_flags(address)) ? 0 : 1, + CallGraph::CacheString(ToString(enum_name)), + Comment::ENUM, /*repeatable=*/false); + } + } + } +#else uint8_t serial; if (is_enum0(get_flags(address))) { int id = get_enum_id(&serial, address, 0); if (id != BADNODE) { comments->emplace_back( address, 0, CallGraph::CacheString(ToString(get_enum_name(id))), - Comment::ENUM, false); + Comment::ENUM, /*repeatable=*/false); } } if (is_enum1(get_flags(address))) { @@ -485,9 +596,10 @@ void GetEnumComments(Address address, if (id != BADNODE) { comments->emplace_back( address, 1, CallGraph::CacheString(ToString(get_enum_name(id))), - Comment::ENUM, false); + Comment::ENUM, /*repeatable=*/false); } } +#endif } bool GetLineComment(Address address, int n, std::string* output) { @@ -578,18 +690,49 @@ struct FunctionCache { if (!function) { return; } +#if IDP_INTERFACE_VERSION >= 900 + tinfo_t frame_tif; + if (!get_func_frame(&frame_tif, function)) { + return; + } + + udt_type_data_t udt_data; + if (!frame_tif.get_udt_details(&udt_data)) { + return; + } + + // IDA sometimes returns excessively large offsets (billions) we must + // prevent looping forever in those cases + size_t last_success = 0; + const size_t max_offset = + std::min(frame_tif.get_size(), + size_t(64 * 1024 /* Max stack size for analysis */)); + for (const udm_t& member : udt_data) { + if (member.offset >= max_offset || last_success - member.offset >= 1024) { + break; + } + if (member.is_special_member()) { + continue; + } + + if (!member.name.empty()) { + local_vars[member.offset] = ToString(member.name); + last_success = member.offset + member.size; + } + } +#else struc_t* frame = get_frame(function); if (!frame) { return; } - // @bug: IDA sometimes returns excessively large offsets (billions) - // we must prevent looping forever in those cases - size_t lastSuccess = 0; - const size_t maxOffset = - std::min(static_cast(get_max_offset(frame)), - static_cast(1024 * 64)); - for (size_t i = 0; i < maxOffset && lastSuccess - i < 1024;) { + // IDA sometimes returns excessively large offsets (billions) we must + // prevent looping forever in those cases + size_t last_success = 0; + const size_t max_offset = std::min( + static_cast(get_max_offset(frame)), + static_cast(64 * 1024 /* Max stack size for analysis */)); + for (size_t i = 0; i < max_offset && last_success - i < 1024;) { const member_t* member = get_member(frame, i); if (!member || is_special_member(member->id)) { ++i; @@ -600,14 +743,15 @@ struct FunctionCache { qstring ida_name(get_member_name(member->id)); if (!ida_name.empty()) { i += std::max(static_cast(1), get_member_size(member)); - lastSuccess = i; + last_success = i; continue; } local_vars[offset] = ToString(ida_name); i += std::max(static_cast(1), get_member_size(member)); - lastSuccess = i; + last_success = i; } +#endif } func_t* function; diff --git a/ida/ui.cc b/ida/ui.cc index aad61b07..119656a6 100644 --- a/ida/ui.cc +++ b/ida/ui.cc @@ -25,6 +25,7 @@ // clang-format off #include "third_party/zynamics/binexport/ida/begin_idasdk.inc" // NOLINT +#include // NOLINT #include // NOLINT #include // NOLINT #include "third_party/zynamics/binexport/ida/end_idasdk.inc" // NOLINT @@ -62,7 +63,11 @@ void WaitBox::ReplaceText(absl::string_view message) const { ReplaceWaitBoxRequest(const WaitBox& wait_box, absl::string_view message) : wait_box(wait_box), message(message) {} +#if IDP_INTERFACE_VERSION >= 900 + ssize_t idaapi execute() override { +#else int idaapi execute() override { +#endif replace_wait_box("%s", FormatMessage(message, wait_box.cancellable_).c_str()); return 0;