标题: IDA微码从mreg名获取数字编号 创建: 2026-01-09 12:12 更新: 链接: https://scz.617.cn/python/202601091212.txt 用IDA微码反控制流平坦化(CFF)时,可能碰上自己构造条件跳转minsn的情形。更复 杂时,像"jcnd lnot(zf.1), @xxx"这种,minsn.l的类型是mop_d。那如何构造 "lnot(zf.1)"呢?我的实现如下: -------------------------------------------------------------------------- def make_special_lnot ( ea ) : mop_l = ida_hexrays.mop_t() mop_l.t = ida_hexrays.mop_r mop_l.r = 1 mop_l.size = 1 mop_r = ida_hexrays.mop_t() mop_r.t = ida_hexrays.mop_z mop_r.size = ida_hexrays.NOSIZE mop_d = ida_hexrays.mop_t() mop_d.t = ida_hexrays.mop_z mop_d.size = 1 minsn = ida_hexrays.minsn_t( ea ) minsn.opcode \ = ida_hexrays.m_lnot minsn.l = mop_l minsn.r = mop_r minsn.d = mop_d return minsn -------------------------------------------------------------------------- 有两处比较莫名其妙。一是mop_d.size未设成NOSIZE,作为对比,mop_r.t是mod_z, mop_r.size是NOSIZE。同样是mod_z类型,为什么mop_d.size特殊了?我也不知道, 只知IDA就这么设的,可在调试IDAPython脚本时手工检查之。 另一处是mop_l.r设为1,这是mreg zf的数字编号。微码有自己的mreg,不要与CPU的 reg搞混。微码编程,操作的是mreg。mreg与reg之间有映射关系,在ida_hexrays.py 中看这几个函数: reg2mreg mreg2reg get_mreg_name 目标样本是AARCH64,mreg zf的数字编号是1。起初我是在调试器中看来的,不方便, 因为下次可能处理其他mreg。暂未找到合适的API,用了个笨但有效的办法,遍历数 字,调用get_mreg_name()获取mreg名,手工建立id->name的映射表,封装函数从表 中根据name获取匹配id。 -------------------------------------------------------------------------- MREG = list(filter(lambda item: '^' not in item[1] and '[' not in item[1], ((i, ida_hexrays.get_mreg_name(i, 4)) for i in range(850)))) def get_mreg_id ( mreg ) : for id, name in MREG : if mreg == name : return id return None -------------------------------------------------------------------------- 遍历数字时,实测最大上限是850,若超过,将触发51283内部错。我看到的MREG长这 样: (0x0, 'cf'), (0x1, 'zf'), (0x2, 'nf'), (0x3, 'vf'), (0x4, 'pf'), (0x5, 'cc'), (0x8, 'w0'), (0x10, 'w1'), (0x18, 'w2'), (0x20, 'w3'), (0x28, 'w4'), (0x30, 'w5'), (0x38, 'w6'), (0x40, 'w7'), (0x48, 'w8'), (0x50, 'w9'), (0x58, 'w10'), (0x60, 'w11'), (0x68, 'w12'), (0x70, 'w13'), (0x78, 'w14'), (0x80, 'w15'), (0x88, 'w16'), (0x90, 'w17'), (0x98, 'w18'), (0xa0, 'w19'), (0xa8, 'w20'), (0xb0, 'w21'), (0xb8, 'w22'), (0xc0, 'w23'), (0xc8, 'w24'), (0xd0, 'w25'), (0xd8, 'w26'), (0xe0, 'w27'), (0xe8, 'w28'), (0xf0, 'w29'), (0xf8, 'w30'), (0x100, 'wzr'), (0x108, 'wsp'), (0x110, 't0'), (0x120, 't1'), (0x130, 't2'), (0x140, 'off'), (0x150, 's0'), (0x160, 's1'), (0x170, 's2'), (0x180, 's3'), (0x190, 's4'), (0x1a0, 's5'), (0x1b0, 's6'), (0x1c0, 's7'), (0x1d0, 's8'), (0x1e0, 's9'), (0x1f0, 's10'), (0x200, 's11'), (0x210, 's12'), (0x220, 's13'), (0x230, 's14'), (0x240, 's15'), (0x250, 's16'), (0x260, 's17'), (0x270, 's18'), (0x280, 's19'), (0x290, 's20'), (0x2a0, 's21'), (0x2b0, 's22'), (0x2c0, 's23'), (0x2d0, 's24'), (0x2e0, 's25'), (0x2f0, 's26'), (0x300, 's27'), (0x310, 's28'), (0x320, 's29'), (0x330, 's30'), (0x340, 's31'), (0x350, 'cs') 曾经懒得在调试器中看,已知w0是8,凭空猜测w0到w9按1步进;但看上表,错得离谱。 幸亏取了全量映射。 最终make_special_lnot()中可以改写成 mop_l.r = get_mreg_id( 'zf' )