rizin/librz/core/analysis_objc.c
Giovanni 4712c82435
refactor: remove RzAsm struct from the public APIs (#6089)
* Remove RzAsm struct from the public APIs
* Forbid plugins from changing RzAsm contents
* Implement archinfo for RX
* Rename archinfo for SH
* Fix tests to use the new output
2026-03-26 01:57:42 +08:00

613 lines
20 KiB
C

// SPDX-FileCopyrightText: 2023 Florian Märkl <info@florianmaerkl.de>
// SPDX-FileCopyrightText: 2019-2020 pancake
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_core.h>
#include "core_private.h"
/* The reference analysis code has been based on Alvaro's
* rzpipe-python script which was based on FireEye script for IDA Pro.
*
* https://www.fireeye.com/blog/threat-research/2017/03/introduction_to_reve.html
*/
typedef struct {
RzCore *core;
HtUP *up;
size_t word_size;
RzBinSection *_selrefs;
RzBinSection *_msgrefs;
RzBinSection *_const;
RzBinSection *_data;
} RzCoreObjc;
const size_t objc2ClassSize = 0x28;
const size_t objc2ClassInfoOffs = 0x20;
const size_t objc2ClassMethSize = 0x18;
const size_t objc2ClassBaseMethsOffs = 0x20;
const size_t objc2ClassMethImpOffs = 0x10;
static void array_add(RzCoreObjc *o, ut64 va, ut64 xrefs_to) {
bool found = false;
RzVector *vec = ht_up_find(o->up, va, &found);
if (!found || !vec) {
vec = rz_vector_new(sizeof(ut64), NULL, NULL);
ht_up_insert(o->up, va, vec);
}
ut64 *addr;
rz_vector_foreach (vec, addr) {
if (xrefs_to == *addr) {
return;
}
}
// extend vector and insert new element
rz_vector_push(vec, &xrefs_to);
}
static inline bool isValid(ut64 addr) {
return (addr != 0LL && addr != UT64_MAX);
}
static inline bool isInvalid(ut64 addr) {
return !isValid(addr);
}
static inline bool inBetween(RzBinSection *s, ut64 addr) {
if (!s || isInvalid(addr)) {
return false;
}
const ut64 from = s->vaddr;
const ut64 to = from + s->vsize;
return RZ_BETWEEN(from, addr, to);
}
static ut32 readDword(RzCoreObjc *objc, ut64 addr, bool *success) {
ut8 buf[4];
*success = rz_io_read_at_mapped(objc->core->io, addr, buf, sizeof(buf));
return rz_read_le32(buf);
}
static ut64 readQword(RzCoreObjc *objc, ut64 addr, bool *success) {
ut8 buf[8] = { 0 };
*success = rz_io_read_at_mapped(objc->core->io, addr, buf, sizeof(buf));
return rz_read_le64(buf);
}
static void objc_analyze(RzCore *core) {
const char *notify = "Analyzing code to find selfref references";
rz_core_notify_begin(core, "%s", notify);
(void)rz_core_analysis_refs(core, 0);
if (!strcmp("arm", rz_config_get(core->config, "asm.arch"))) {
const bool emu_lazy = rz_config_get_i(core->config, "emu.lazy");
rz_config_set_i(core->config, "emu.lazy", true);
rz_core_analysis_esil_default(core);
rz_config_set_i(core->config, "emu.lazy", emu_lazy);
}
rz_core_notify_done(core, "%s", notify);
}
static ut64 getRefPtr(RzCoreObjc *o, ut64 classMethodsVA, bool *rfound) {
*rfound = false;
bool readSuccess;
ut64 namePtr = readQword(o, classMethodsVA, &readSuccess);
if (!readSuccess) {
return UT64_MAX;
}
size_t cnt = 0;
ut64 ref = UT64_MAX;
bool isMsgRef = false;
RzVector *vec = ht_up_find(o->up, namePtr, rfound);
if (!*rfound || !vec) {
*rfound = false;
return false;
}
ut64 *addr;
rz_vector_foreach (vec, addr) {
const ut64 at = *addr;
if (inBetween(o->_selrefs, at)) {
isMsgRef = false;
ref = at;
} else if (inBetween(o->_msgrefs, at)) {
isMsgRef = true;
ref = at;
} else if (inBetween(o->_const, at)) {
cnt++;
}
}
if (cnt > 1 || ref == 0 || ref == UT64_MAX) {
*rfound = false;
return UT64_MAX;
}
return isMsgRef ? ref - 8 : ref;
}
static bool objc_build_refs(RzCoreObjc *objc) {
ut64 off;
rz_return_val_if_fail(objc->_const && objc->_selrefs, false);
const ut64 va_const = objc->_const->vaddr;
size_t ss_const = objc->_const->vsize;
const ut64 va_selrefs = objc->_selrefs->vaddr;
size_t ss_selrefs = objc->_selrefs->vsize;
// TODO: check if ss_const or ss_selrefs are too big before going further
size_t maxsize = RZ_MAX(ss_const, ss_selrefs);
ut8 *buf = calloc(1, maxsize);
if (!buf) {
return false;
}
const size_t word_size = objc->word_size; // assuming 8 because of the read_le64
if (!rz_io_read_at_mapped(objc->core->io, objc->_const->vaddr, buf, ss_const)) {
RZ_LOG_ERROR("aao: Cannot read the whole const section %zu\n", ss_const);
return false;
}
for (off = 0; off + word_size < ss_const && (off + 8) < maxsize; off += word_size) {
ut64 va = va_const + off;
ut64 xrefs_to = rz_read_le64(buf + off);
if (isValid(xrefs_to)) {
array_add(objc, va, xrefs_to);
}
}
if (!rz_io_read_at_mapped(objc->core->io, va_selrefs, buf, ss_selrefs)) {
RZ_LOG_ERROR("aao: Cannot read the whole selrefs section\n");
return false;
}
for (off = 0; off + word_size < ss_selrefs && (off + 8) < maxsize; off += word_size) {
ut64 va = va_selrefs + off;
ut64 xrefs_to = rz_read_le64(buf + off);
if (isValid(xrefs_to)) {
array_add(objc, xrefs_to, va);
}
}
free(buf);
return true;
}
static RzCoreObjc *core_objc_new(RzCore *core) {
RzBinObject *obj = rz_bin_cur_object(core->bin);
const RzPVector *sections = obj ? rz_bin_object_get_sections_all(obj) : NULL;
if (!sections) {
return false;
}
RzCoreObjc *o = RZ_NEW0(RzCoreObjc);
o->core = core;
o->word_size = rz_asm_is_bits(core->rasm, 64) ? 8 : 4;
if (o->word_size != 8) {
RZ_LOG_WARN("aao is experimental on 32bit binaries\n");
}
RzBinSection *s;
void **iter;
rz_pvector_foreach (sections, iter) {
s = *iter;
const char *name = s->name;
if (strstr(name, "__objc_data")) {
o->_data = s;
} else if (strstr(name, "__objc_selrefs")) {
o->_selrefs = s;
} else if (strstr(name, "__objc_msgrefs")) {
o->_msgrefs = s;
} else if (strstr(name, "__objc_const")) {
o->_const = s;
}
}
if (!o->_const || !o->_selrefs || ((o->_selrefs || o->_msgrefs) && !(o->_data && o->_const))) {
free(o);
return NULL;
}
o->up = ht_up_new(NULL, (HtUPFreeValue)rz_vector_free);
return o;
}
static void core_objc_free(RzCoreObjc *o) {
if (o) {
ht_up_free(o->up);
free(o);
}
}
static bool objc_find_refs(RzCore *core) {
RzCoreObjc *objc = core_objc_new(core);
if (!objc) {
RZ_LOG_DEBUG("Could not find necessary Objective-C sections...\n");
return false;
}
if (!objc_build_refs(objc)) {
core_objc_free(objc);
return false;
}
const char *notify = "Parsing metadata in ObjC to find hidden xrefs";
rz_core_notify_begin(core, "%s", notify);
size_t total_xrefs = 0;
bool readSuccess = true;
for (ut64 off = 0; off < objc->_data->vsize && readSuccess; off += objc2ClassSize) {
if (!readSuccess || rz_cons_is_breaked()) {
break;
}
ut64 va = objc->_data->vaddr + off;
// XXX do a single rz_io_read_at_mapped() and just rz_read_le64() here
ut64 classRoVA = readQword(objc, va + objc2ClassInfoOffs, &readSuccess);
if (!readSuccess || isInvalid(classRoVA)) {
continue;
}
ut64 classMethodsVA = readQword(objc, classRoVA + objc2ClassBaseMethsOffs, &readSuccess);
if (!readSuccess || isInvalid(classMethodsVA)) {
continue;
}
ut32 count = readDword(objc, classMethodsVA + 4, &readSuccess);
if (!readSuccess || ((ut32)count == UT32_MAX)) {
continue;
}
classMethodsVA += 8; // advance to start of class methods array
ut64 to = classMethodsVA + (objc2ClassMethSize * count);
if (classMethodsVA > to || classMethodsVA + 0xfffff < to) {
RZ_LOG_WARN("objc: the input binary might be malformed or this could be a bug.\n");
continue;
}
for (va = classMethodsVA; va < to; va += objc2ClassMethSize) {
if (rz_cons_is_breaked()) {
break;
}
bool found = false;
ut64 selRefVA = getRefPtr(objc, va, &found);
if (!found) {
continue;
}
bool succ = false;
ut64 funcVA = readQword(objc, va + objc2ClassMethImpOffs, &succ);
if (!succ) {
break;
}
RzList *list = rz_analysis_xrefs_get_to(core->analysis, selRefVA);
if (list) {
RzListIter *iter;
RzAnalysisXRef *xref;
rz_list_foreach (list, iter, xref) {
rz_analysis_xrefs_set(core->analysis, xref->from, funcVA, RZ_ANALYSIS_XREF_TYPE_CODE);
total_xrefs++;
}
}
rz_list_free(list);
}
}
rz_core_notify_done(core, "%s", notify);
const ut64 va_selrefs = objc->_selrefs->vaddr;
const ut64 ss_selrefs = va_selrefs + objc->_selrefs->vsize;
rz_core_notify_begin(core, "Found %zu objc xrefs...", total_xrefs);
size_t total_words = 0;
const size_t word_size = objc->word_size;
for (ut64 a = va_selrefs; a < ss_selrefs; a += word_size) {
rz_meta_set(core->analysis, RZ_META_TYPE_DATA, a, word_size, NULL);
total_words++;
}
rz_core_notify_done(core, "Found %zu objc xrefs in %zu dwords.", total_xrefs, total_words);
core_objc_free(objc);
return true;
}
RZ_API bool rz_core_analysis_objc_refs(RzCore *core, bool auto_analysis) {
rz_return_val_if_fail(core, 0);
if (!auto_analysis) {
objc_analyze(core);
}
return objc_find_refs(core);
}
static const ut8 objc_stubs_pattern_x86_64[] = {
0x48, 0x8b, 0x35, 0x00, 0x00, 0x00, 0x00, // mov rsi, qword [<str pointer addr>]
0xff, 0x25, 0x00, 0x00, 0x00, 0x00 // jmp qword reloc.objc_msgSend
};
static const ut8 objc_stubs_mask_x86_64[] = {
0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, // mov rsi, qword [<str pointer addr>]
0xff, 0xff, 0x00, 0x00, 0x00, 0x00 // jmp qword reloc.objc_msgSend
};
// clang -arch arm64e ...
static const ut8 objc_stubs_pattern_arm64e[] = {
0x01, 0x00, 0x00, 0x90, // adrp x1, <section.__DATA.__objc_const>
0x21, 0x00, 0x40, 0xf9, // ldr x1, [x1, <selector string ptr offset>]
0x11, 0x00, 0x00, 0x90, // adrp x17, <reloc base>
0x31, 0x02, 0x00, 0x91, // add x17, x17, <reloc offset>
0x30, 0x02, 0x40, 0xf9, // ldr x16, [x17]
0x11, 0x0a, 0x1f, 0xd7, // braa x16, x17
0x20, 0x00, 0x20, 0xd4, // brk 1
0x20, 0x00, 0x20, 0xd4 // brk 1
};
static const ut8 objc_stubs_mask_arm64e[] = {
0x1f, 0x00, 0x00, 0x9f, // adrp x1, <section.__DATA.__objc_const>
0xff, 0x03, 0xc0, 0xff, // ldr x1, [x1, <selector string ptr offset>]
0x1f, 0x00, 0x00, 0x9f, // adrp x17, <reloc base>
0xff, 0x03, 0xc0, 0xff, // add x17, x17, <reloc offset>
0xff, 0xff, 0xff, 0xff, // ldr x16, [x17]
0xff, 0xff, 0xff, 0xff, // braa x16, x17
0xff, 0xff, 0xff, 0xff, // brk 1
0xff, 0xff, 0xff, 0xff // brk 1
};
// clang -arch arm64 ...
static const ut8 objc_stubs_pattern_arm64[] = {
0x01, 0x00, 0x00, 0x90, // adrp x1, <section.__DATA.__objc_const>
0x21, 0x00, 0x40, 0xf9, // ldr x1, [x1, <selector string ptr offset>]
0x10, 0x00, 0x00, 0x90, // adrp x16, <reloc base>
0x10, 0x02, 0x40, 0xf9, // ldr x16, [x17, <reloc offset>]
0x00, 0x02, 0x1f, 0xd6, // br x16
0x20, 0x00, 0x20, 0xd4, // brk 1
0x20, 0x00, 0x20, 0xd4, // brk 1
0x20, 0x00, 0x20, 0xd4 // brk 1
};
static const ut8 objc_stubs_mask_arm64[] = {
0x1f, 0x00, 0x00, 0x9f, // adrp x1, <section.__DATA.__objc_const>
0xff, 0x03, 0xc0, 0xff, // ldr x1, [x1, <selector string ptr offset>]
0x1f, 0x00, 0x00, 0x9f, // adrp x16, <reloc base>
0xff, 0x03, 0xc0, 0xff, // ldr x16, [x16, <reloc offset>]
0xff, 0xff, 0xff, 0xff, // br x16
0xff, 0xff, 0xff, 0xff, // brk 1
0xff, 0xff, 0xff, 0xff, // brk 1
0xff, 0xff, 0xff, 0xff // brk 1
};
// clang -arch arm64 -Wl,-objc_stubs_small ... (-arch arm64e -Wl,-objc_stubs_small exists too but generates nonsense)
static const ut8 objc_stubs_pattern_arm64_small[] = {
0x01, 0x00, 0x00, 0x90, // adrp x1, <section.__DATA.__objc_const>
0x21, 0x00, 0x40, 0xf9, // ldr x1, [x1, <selector string ptr offset>]
0x00, 0x00, 0x00, 0x14 // b <sym.imp.objc_msgSend>
};
static const ut8 objc_stubs_mask_arm64_small[] = {
0x1f, 0x00, 0x00, 0x9f, // adrp x1, <section.__DATA.__objc_const>
0xff, 0x03, 0xc0, 0xff, // ldr x1, [x1, <selector string ptr offset>]
0x00, 0x00, 0x00, 0xfc, // b <sym.imp.objc_msgSend>
};
static const ut8 objc_stubs_pattern_arm64_32[] = {
0x01, 0x00, 0x00, 0x90, // adrp x1, <section.__DATA.__objc_const>
0x21, 0x00, 0x40, 0xb9, // ldr w1, [x1, <selector string ptr offset>]
0x10, 0x00, 0x00, 0x90, // adrp x16, <reloc base>
0x10, 0x02, 0x40, 0xb9, // ldr w16, [x17, <reloc offset>]
0x00, 0x02, 0x1f, 0xd6, // br x16
0x20, 0x00, 0x20, 0xd4, // brk 1
0x20, 0x00, 0x20, 0xd4, // brk 1
0x20, 0x00, 0x20, 0xd4 // brk 1
};
static const ut8 objc_stubs_mask_arm64_32[] = {
0x1f, 0x00, 0x00, 0x9f, // adrp x1, <section.__DATA.__objc_const>
0xff, 0x03, 0xc0, 0xff, // ldr w1, [x1, <selector string ptr offset>]
0x1f, 0x00, 0x00, 0x9f, // adrp x16, <reloc base>
0xff, 0x03, 0xc0, 0xff, // ldr w16, [x16, <reloc offset>]
0xff, 0xff, 0xff, 0xff, // br x16
0xff, 0xff, 0xff, 0xff, // brk 1
0xff, 0xff, 0xff, 0xff, // brk 1
0xff, 0xff, 0xff, 0xff // brk 1
};
static const ut8 objc_stubs_pattern_arm64_32_small[] = {
0x01, 0x00, 0x00, 0x90, // adrp x1, <section.__DATA.__objc_const>
0x21, 0x00, 0x40, 0xb9, // ldr w1, [x1, <selector string ptr offset>]
0x00, 0x00, 0x00, 0x14 // b <sym.imp.objc_msgSend>
};
static const ut8 objc_stubs_mask_arm64_32_small[] = {
0x1f, 0x00, 0x00, 0x9f, // adrp x1, <section.__DATA.__objc_const>
0xff, 0x03, 0xc0, 0xff, // ldr w1, [x1, <selector string ptr offset>]
0x00, 0x00, 0x00, 0xfc, // b <sym.imp.objc_msgSend>
};
static ut64 arm64_adrp_get_addr(ut64 pc, ut32 op) {
ut64 imm = (((op >> 5) & 0x7ffff) << 2) | ((op >> 29) & 3);
imm <<= 12;
if (imm & (1ULL << 32)) {
// sign extend
imm = (((ut64)-1LL) << 33) | imm;
}
return (pc & ~0xfffULL) + imm;
}
static ut64 arm64_get_imm12(ut32 op) {
return (op >> 10) & 0xfff;
}
/**
* Try to read the actual selector string through a pointer
*
* \param str_ptr_addr the address where a pointer to the selector string is
* \param ptr_size size of the pointer in bytes
* \param selector_out buffer to write the selector string to
* \param selector_out_sz buffer size of \p selector_out
*/
static bool read_selector_indirect(RzIO *io, ut64 str_ptr_addr, size_t ptr_size, char *selector_out, size_t selector_out_sz) {
ut8 ptr[8];
if (!rz_io_read_at_mapped(io, str_ptr_addr, ptr, ptr_size)) {
return false;
}
ut64 str_addr = ptr_size == 4 ? rz_read_le32(ptr) : rz_read_le64(ptr);
if (rz_io_nread_at(io, str_addr, (ut8 *)selector_out, selector_out_sz - 1) <= 0) {
return false;
}
selector_out[selector_out_sz - 1] = '\0';
return true;
}
static bool objc_stubs_extract_arm64(RzCore *core, ut8 *stub_contents, ut64 addr, char *selector_out, size_t selector_out_sz, size_t ptr_size) {
ut64 str_base = arm64_adrp_get_addr(addr, rz_read_le32(stub_contents));
ut64 str_off = arm64_get_imm12(rz_read_le32(stub_contents + 4)) * ptr_size;
ut64 str_ptr = str_base + str_off;
return read_selector_indirect(core->io, str_ptr, ptr_size, selector_out, selector_out_sz);
}
static bool objc_stubs_extract_arm64_64(RzCore *core, ut8 *stub_contents, ut64 addr, char *selector_out, size_t selector_out_sz) {
return objc_stubs_extract_arm64(core, stub_contents, addr, selector_out, selector_out_sz, 8);
}
static bool objc_stubs_extract_arm64_32(RzCore *core, ut8 *stub_contents, ut64 addr, char *selector_out, size_t selector_out_sz) {
return objc_stubs_extract_arm64(core, stub_contents, addr, selector_out, selector_out_sz, 4);
}
static bool objc_stubs_extract_x86_64(RzCore *core, ut8 *stub_contents, ut64 addr, char *selector_out, size_t selector_out_sz) {
// mov rsi, qword [<str pointer addr>]
// Encoded as:
// 48 - REX opcode for 64bit operand
// 8b - opcode: MOV Gv, Ev
// 35 - ModRM:
// reg = 6 = rsi
// mod / r/m = 0 / 5 = disp32
// followed by 4 bytes forming the disp32
st32 disp32 = rz_read_le32(stub_contents + 3);
ut64 str_ptr = addr + 7 + (st64)disp32;
return read_selector_indirect(core->io, str_ptr, 8, selector_out, selector_out_sz);
}
typedef struct {
const ut8 *pattern;
const ut8 *mask;
size_t size;
bool (*extract_selector_str)(RzCore *core, ut8 *stub_contents, ut64 addr, char *selector_out, size_t selector_out_sz);
} ObjcStubsPattern;
#define PATTERN(name, extract_fn) \
{ objc_stubs_pattern_##name, objc_stubs_mask_##name, sizeof(objc_stubs_pattern_##name), extract_fn }
static const ObjcStubsPattern objc_stubs_patterns[] = {
PATTERN(arm64e, objc_stubs_extract_arm64_64),
PATTERN(arm64, objc_stubs_extract_arm64_64),
PATTERN(arm64_small, objc_stubs_extract_arm64_64),
PATTERN(arm64_32, objc_stubs_extract_arm64_32),
PATTERN(arm64_32_small, objc_stubs_extract_arm64_32),
PATTERN(x86_64, objc_stubs_extract_x86_64),
{ 0 }
};
#undef PATTERN
static bool flag_with_space_exists_at(RzCore *core, ut64 addr, RzSpace *space) {
const RzList *existing = rz_flag_get_list(core->flags, addr);
if (!existing) {
return false;
}
RzListIter *it;
RzFlagItem *fi;
rz_list_foreach (existing, it, fi) {
if (fi->space && fi->space == space) {
// Do not create a flag if there is already a symbol (unstripped bin)
return true;
}
}
return false;
}
static void apply_selector_stub_at(RzCore *core, ut64 addr, ut32 size, char *selector) {
char name[512];
RzFlagItem *fi = rz_flag_set_next(core->flags, rz_strf(name, "stub.objc_msgSend$%s", selector), addr, size);
if (!fi) {
return;
}
rz_flag_item_set_realname(fi, rz_strf(name, "objc_msgSend$%s", selector));
// If there is already a function (e.g. from aa), rename it too
RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, addr);
if (fcn) {
rz_core_analysis_function_rename(core, addr, fi->name);
}
}
static void analyze_objc_stubs(RzCore *core, ut64 start, ut64 size) {
// Selecting only a subset of the known patterns would be possible by checking the architecture.
// But as long as there are only a few, checking them all for all binaries is ok.
const ObjcStubsPattern *patterns = objc_stubs_patterns;
size_t max_pattern_sz = 0;
size_t min_pattern_sz = SIZE_MAX;
for (const ObjcStubsPattern *pattern = patterns; pattern->pattern; pattern++) {
if (pattern->size > max_pattern_sz) {
max_pattern_sz = pattern->size;
}
if (pattern->size < min_pattern_sz) {
min_pattern_sz = pattern->size;
}
}
ut64 offset = 0;
ut8 *stub_contents = malloc(max_pattern_sz);
if (!stub_contents) {
return;
}
RzSpace *symbols_space = rz_spaces_get(&core->flags->spaces, RZ_FLAGS_FS_SYMBOLS);
rz_flag_space_push(core->flags, "objc-stubs");
rz_cons_break_push(NULL, NULL);
while (offset + min_pattern_sz <= size && !rz_cons_is_breaked()) {
ut64 addr = start + offset;
size_t read_sz = RZ_MIN(max_pattern_sz, size - offset);
if (!rz_io_read_at_mapped(core->io, addr, stub_contents, read_sz)) {
RZ_LOG_ERROR("Failed to read in __objc_stubs at 0x%" PFMT64x "\n", addr);
break;
}
ut64 stride = 0;
for (const ObjcStubsPattern *pattern = patterns; pattern->pattern; pattern++) {
if (pattern->size > read_sz) {
// not enough bytes remaining in section for this pattern to match
continue;
}
if (!rz_mem_eq_masked(stub_contents, pattern->pattern, pattern->mask, pattern->size)) {
// no match
continue;
}
stride = pattern->size;
if (flag_with_space_exists_at(core, addr, symbols_space)) {
// there is already a symbol, no need to annotate
break;
}
char selector[256];
if (!pattern->extract_selector_str(core, stub_contents, addr, selector, sizeof(selector))) {
break;
}
// An optional sanity check here would be to also assert that really objc_msgSend is
// called and no other function.
apply_selector_stub_at(core, addr, pattern->size, selector);
break;
}
if (!stride) {
RZ_LOG_ERROR("Failed to match any known pattern against __objc_stubs contents. "
"If this is not a manually modified binary, please consider opening an "
"issue to let the rizin team know about this new format.\n");
// no pattern matched, cancel the entire search because the section is not in a known format.
break;
}
offset += stride;
}
rz_cons_break_pop();
rz_flag_space_pop(core->flags);
free(stub_contents);
}
/**
* Analyze the __objc_stubs section and assign names to all detected
* selector stubs found
*/
RZ_API void rz_core_analysis_objc_stubs(RzCore *core) {
rz_return_if_fail(core);
RzBinObject *obj = rz_bin_cur_object(core->bin);
const RzPVector *sections = obj ? rz_bin_object_get_sections_all(obj) : NULL;
if (!sections) {
return;
}
RzBinSection *stubs_section;
void **iter;
rz_pvector_foreach (sections, iter) {
stubs_section = *iter;
if (strstr(stubs_section->name, "__objc_stubs")) {
goto found;
}
}
RZ_LOG_ERROR("__objc_stubs section not found for analysis\n");
return;
found:
analyze_objc_stubs(core, stubs_section->vaddr, stubs_section->vsize);
}