Fix Inconsistent ROP gadget info between detail printing and listing#6108
Fix Inconsistent ROP gadget info between detail printing and listing#6108MrQuantum1915 wants to merge 0 commit intorizinorg:devfrom
Conversation
Shouldn't this be fixed in |
thanks!!! the gadget with I checked the type of one of instruction: the type is i checked // register is $ra, so jmp is a return
if (insn->detail->mips.operands[0].reg == MIPS_REG_RA) {
op->type = RZ_ANALYSIS_OP_TYPE_RET;
ctx->t9_pre = UT64_MAX;
}so the reason is that the enum is not matching. And 331 belongs to Our test binary was 22 is for 32 bit adding a check for This is a mistake because for Capstone version >=6 (which split 32bit and 64 bit reg classes), we should compare it with 64 bit's enum not 32 bit. Actually I analysed whole file to see any other such mistake, i found a few other blind spots where we are missing these modern Capstone checks. (NOTE: in somecases we correctly check for 64bit like line 391, but in other we are missing the check, which can actually break analyses if we test for 64bit mips binary): List:
Also Capstone version 6 introduced nanoMIPS, so I think we should add If you agree, i would like to open a NEW PR to fix mips analysis file with this fixes. This will automatically fix issue of Also how would you prefer I handle the Capstone v6 enums?
|
|
[Ignoring my changes in PR] Regardless of my previous comment on const RzCoreAsmHit *hit_last = (RzCoreAsmHit *)rz_list_last_val(hitlist);
if (!is_ret_gadget(core, hit_last, crop)) {
return rop_gadget_info;
}MIPS gadget has But currently we are only checking if LAST instruction is const RzCoreAsmHit *hit_last = (RzCoreAsmHit *)rz_list_last_val(hitlist);
const RzCoreAsmHit *hit_prev = (RzCoreAsmHit *)rz_list_get_n(hitlist, rz_list_length(hitlist) - 2);
if (!is_ret_gadget(core, hit_last, crop)) {
if (!hit_prev || !is_ret_gadget(core, hit_prev, crop)) {
return rop_gadget_info;
}
}NOTE: this is just temp fix, like using After this change Also we need to decide now whether
|
|
A good point. JOP and COP are sometimes useful but quite niche, thus calls should not be a part of the default ROP chain listing. I suggest moving them into a separate command probably. Maybe part of the new |
hmm...makes sense. So for ROP we should remove JMP and CALL types from checks. static bool is_end_gadget(const RzAnalysisOp *aop, const ut8 crop) {
if (aop->family == RZ_ANALYSIS_OP_FAMILY_SECURITY) {
return false;
}
switch (aop->type) {
case RZ_ANALYSIS_OP_TYPE_TRAP:
case RZ_ANALYSIS_OP_TYPE_RET:
case RZ_ANALYSIS_OP_TYPE_UCALL:
case RZ_ANALYSIS_OP_TYPE_RCALL:
case RZ_ANALYSIS_OP_TYPE_ICALL:
case RZ_ANALYSIS_OP_TYPE_IRCALL:
case RZ_ANALYSIS_OP_TYPE_UJMP:
case RZ_ANALYSIS_OP_TYPE_RJMP:
case RZ_ANALYSIS_OP_TYPE_IJMP:
case RZ_ANALYSIS_OP_TYPE_IRJMP:
case RZ_ANALYSIS_OP_TYPE_JMP:
case RZ_ANALYSIS_OP_TYPE_CALL:
if (crop) {
return is_cond_end_gadget(aop);
}
return true;
default:
return false;
}
}For ROP we should keep: case RZ_ANALYSIS_OP_TYPE_TRAP:
case RZ_ANALYSIS_OP_TYPE_RET:For COP: case RZ_ANALYSIS_OP_TYPE_UCALL:
case RZ_ANALYSIS_OP_TYPE_RCALL:
case RZ_ANALYSIS_OP_TYPE_ICALL:
case RZ_ANALYSIS_OP_TYPE_IRCALL:
case RZ_ANALYSIS_OP_TYPE_CALL:For JOP: case RZ_ANALYSIS_OP_TYPE_UJMP:
case RZ_ANALYSIS_OP_TYPE_RJMP:
case RZ_ANALYSIS_OP_TYPE_IJMP:
case RZ_ANALYSIS_OP_TYPE_IRJMP:
case RZ_ANALYSIS_OP_TYPE_JMP:We can then use them in their respective check IMP: By doing so I am assuming that specific architecture analysis plugins correctly promote their specific return idioms (like MIPS We can implement this in a new /J : List all indirect branch gadgets (both JOP and COP).
/Jj: List only pure JOP gadgets (ends in jmp).
/Jc: List only COP gadgets (ends in call).
Does this make sense? Should I proceed? |
Yes, exactly. Be sure to mimic |
eb5ad1e to
91174eb
Compare
91174eb to
6a41c38
Compare





Your checklist for this pull request
RZ_APIfunction and struct this PR changes.RZ_API).Detailed description
This PR, closes issue #5491 about inconsistant output of Listing gadgets vs Detailed info printing of gadgets .
Tracing the bug
When we run
/R' it lists a gadgets upto0x00002d2cbut in/Rgit only outputs till0x000020e0`. (#5491)The count of gadgets reduces :
When we run
/Rand see output at0x000020e0, it hasretinstruction (return)And it correctly get listed in
/Rgalso.So seeing instruction at
0x00002d2cwe see that it hascallinstruction.I confirmed this behavior for other gadgets too.
So the problem is that in
/Rganalysis only gadgets havingretinstruction are getting listed.Now tracing the source code:
Rgis handled byrz_cmd_detail_gadget_handlerthe call Hierarchy gets us to

perform_gadget_analysisIn the
perform_gadget_analysisfunction on line 1408, it explicitly checks forretgadget!:now

/Ris handled byrz_cmd_info_gadget_handler, it searches for rop and computes end_gadget_listIn
compute_end_gadget_liston line 1519, it checks for end_gadget_list, instead of ret_gadget in/Rg:NOw in is_end_gadget function, it includes many more instructions along with
ret.Hence
/Rshows more gadgets compared to/Rgbecause the/Rgdrops majority of gadgets in analysis phase./Rgshould analyse all the gadgets in detail instead of filtering out the ones withoutretinstructio, because after all the result of/Rand/Rgshould converge to a single truth.Firstly I tried to replace
is_ret_gadgetfunction by callingis_end_gadget inis_end_gadget_hit` wrapper function :For
ARMbinary the result is OK,NOTE: that running detailed analysis reduced gadgets from 745 to 738 because initially aaa pipeline is does not emulate every path, whereas /Rg runs IL emulation on every gadget, and hence it discards some gadgets due to unreachanble code etc..
But for MIPS binary it still breaks:
Running
/Rshows that many gadgets end withnopno operation (delay slot) , which is not covered inis_end_gadgetcheck:But if you check
compute_end_gadget_listfunction at line 1522, it clearly handles the delay slot case and hence/Rshows that gadgets , whereas our fix of calling onlyis_end_gadgetdoes not handle delay slot explicitly.Actually doing is_end_gadget check is redundant.
Because the hitlist passed to
perform_gadget_analysisfunctions is generated byconstruct_rop_gadgetfunction. And this function uses theend_list(pre-computed bycompute_end_gadget_list) which identifies all valid terminators (RET, CALL, JMP, etc) and correctly handles delay slots as seen above.The valid flag only becomes
truewhen the gadget ends exactly at one of these pre-validated terminators.So doing
is_end_gadget()check insideperform_gadget_analysisfunction is REDUNDANT.FINAL FIX
The most minimal and correct fix is to remove the check entirely. We trust the search engine to only deliver valid gadgets for analysis. This unifies the logic and ensures that /Rg, /Rk, /Rs, and /Rl all work consistently across all architectures. And as seen in Test plan section below, the outputs of
/Rand/Rgconverges to a single truth!NOTE: Also as now
/Rglists so many more gadgets that were ignored previously, I have to change the expected outputs for many tests in rop regression testcmd_ropusing-iinteractive mode....
Test plan
After this PR, the values of /R and /Rg would converge instead of showing different outputs as in #5491
NOTE: As now /Rg has to emulate many more gadgets this test will take some time to finish, because they are 68192 gadgets!!
...
Closing issues
closes #5491
...