首页
社区
课程
招聘
[原创]记一次修复Ghidra反编译混淆代码的Bug经历
发表于: 2023-9-5 10:24 10990

[原创]记一次修复Ghidra反编译混淆代码的Bug经历

2023-9-5 10:24
10990

最近在尝试使用Ghidra来对混淆代码进行反编译,然而在反编译一段代码的时候出现了问题,这里记录一下修复的过程,希望对其他人有起到一点帮助。

为了方便描述代码经过了简化处理,大概是下面这个样子的:

通过实际运行这段代码很明显eax最终的值是0x2,然而Ghidra在这里算出来的eax是0x1。

我们调试一下Ghidra的反编译过程,在Action::perform函数里面插个日志打印代码,将执行Action后的pcode全部都打印出来,从而快速定位到最关键的Action。

最终发现是执行oppool1这个动作后,pcode发生了错误,错误之前的pcode是下面这样的;

然而执行完oppool1之后,pcode直接变成了

oppool1其实是Ghidra里面的核心Action之一,里面包含了一系列优化规则,再进一步调试oppool1里面的规则,可以定位到是RulePropagateCopy这个规则,RulePropagateCopy会对CPUI_COPY类型的节点进行传播,规则将

转换成了

分析到这里,Ghidra反编译失败的原因就很明显了,在将*(ram,u0x00002880(0x00401019:e))识别成堆栈变量s0xfffffff4之前就进行了传播优化。

那么怎么解决这个问题呢,Ghidra的oppool1老实说我感觉就像是一个大杂烩,啥规则都往里面扔,里面的优化逻辑就是一直优化,优化到pcode没有发生变化为止,然而优化顺序似乎不怎么能保证,因此我决定自己编写规则。

模仿oppool2中的RuleStoreVarnode规则,这个规则是尝试将store节点转换为stack变量,我们也写一个RuleVmpStoreVarnode规则,在每次遇到CPUI_STORE类型的pcode的时候,就尝试去计算写入的地址是否是堆栈。规则代码如下:

为了计算堆栈的偏移,写了一个类VmpStackEvaluator,核心思路就是不断遍历def节点,判断最终是否为esp + xxx这种偏移。

不管效率怎么样,往oppool1里面扔进去这个规则,执行后最终的代码变成了:

也算是勉强完成了修复吧

0x00401009:2:   u0x10000014(0x00401009:2) = #0x2f9d6707
0x00401009:1e:  s0xfffffffc:2(0x00401009:1e) = SUB42(u0x10000014(0x00401009:2),#0x0)
0x0040100e:5:   u0x10000010(0x0040100e:5) = #0xd915b19b
0x0040100e:1d:  s0xfffffffa:2(0x0040100e:1d) = SUB42(u0x10000010(0x0040100e:5),#0x2)
0x00401013:8:   s0xfffffff4(0x00401013:8) = #0x1
0x00401015:1b:  u0x1000000c(0x00401015:1b) = CONCAT22(s0xfffffffc:2(0x00401009:1e),s0xfffffffa:2(0x0040100e:1d))
0x00401015:a:   u0x00007a00(0x00401015:a) = u0x1000000c(0x00401015:1b)
0x00401019:c:   u0x00002700(0x00401019:c) = ESP(i) + #0x98f826df
0x00401019:e:   u0x00002880(0x00401019:e) = u0x00002700(0x00401019:c) + u0x00007a00(0x00401015:a)
0x00401019:1a:  s0xfffffff4(0x00401019:1a) = s0xfffffff4(0x00401013:8) [] i0x00401019:10(free)
0x00401019:10*(ram,u0x00002880(0x00401019:e)) = #0x2
0x00401024:11:  EAX(0x00401024:11) = s0xfffffff4(0x00401019:1a)
0x00401025:15return(EAX(0x00401024:11))
0x00401009:2:   u0x10000014(0x00401009:2) = #0x2f9d6707
0x00401009:1e:  s0xfffffffc:2(0x00401009:1e) = SUB42(u0x10000014(0x00401009:2),#0x0)
0x0040100e:5:   u0x10000010(0x0040100e:5) = #0xd915b19b
0x0040100e:1d:  s0xfffffffa:2(0x0040100e:1d) = SUB42(u0x10000010(0x0040100e:5),#0x2)
0x00401013:8:   s0xfffffff4(0x00401013:8) = #0x1
0x00401015:1b:  u0x1000000c(0x00401015:1b) = CONCAT22(s0xfffffffc:2(0x00401009:1e),s0xfffffffa:2(0x0040100e:1d))
0x00401015:a:   u0x00007a00(0x00401015:a) = u0x1000000c(0x00401015:1b)
0x00401019:c:   u0x00002700(0x00401019:c) = ESP(i) + #0x98f826df
0x00401019:e:   u0x00002880(0x00401019:e) = u0x00002700(0x00401019:c) + u0x00007a00(0x00401015:a)
0x00401019:1a:  s0xfffffff4(0x00401019:1a) = s0xfffffff4(0x00401013:8) [] i0x00401019:10(free)
0x00401019:10*(ram,u0x00002880(0x00401019:e)) = #0x2
0x00401024:11:  EAX(0x00401024:11) = s0xfffffff4(0x00401019:1a)
0x00401025:15return(EAX(0x00401024:11))
0x00401019:e:   u0x00002880(0x00401019:e) = ESP(i) + #0xfffffff4
0x00401019:10*(ram,u0x00002880(0x00401019:e)) = #0x2
0x00401024:11:  EAX(0x00401024:11) = #0x1
0x00401025:15return(EAX(0x00401024:11)) EAX(0x00401024:11)
0x00401019:e:   u0x00002880(0x00401019:e) = ESP(i) + #0xfffffff4
0x00401019:10*(ram,u0x00002880(0x00401019:e)) = #0x2
0x00401024:11:  EAX(0x00401024:11) = #0x1
0x00401025:15return(EAX(0x00401024:11)) EAX(0x00401024:11)
0x00401013:8:   s0xfffffff4(0x00401013:8) = #0x1
0x00401024:11:  EAX(0x00401024:11) = s0xfffffff4(0x00401013:8)
0x00401013:8:   s0xfffffff4(0x00401013:8) = #0x1
0x00401024:11:  EAX(0x00401024:11) = s0xfffffff4(0x00401013:8)
0x00401024:11:  EAX(0x00401024:11) = #0x1
0x00401024:11:  EAX(0x00401024:11) = #0x1
class RuleVmpStoreVarnode : public Rule {
public:
    RuleVmpStoreVarnode(const string& g) : Rule(g, 0, "vmpstorevarnode") {} ///< Constructor
    virtual Rule* clone(const ActionGroupList& grouplist) const {
        if (!grouplist.contains(getGroup())) return (Rule*)0;
        return new RuleVmpStoreVarnode(getGroup());
    }
    virtual void getOpList(vector<uint4>& oplist) const;
    virtual int4 applyOp(PcodeOp* op, Funcdata& data);
};
void RuleVmpStoreVarnode::getOpList(vector<uint4>& oplist) const
{
    oplist.push_back(CPUI_STORE);
}
int4 RuleVmpStoreVarnode::applyOp(PcodeOp* op, Funcdata& data)
{
    VmpStackEvaluator evalCall;
    Varnode* offvn = op->getIn(1);
    int stackOffset = 0x0;
    if (!evalCall.EvaluateStackOffset(data, offvn, stackOffset)) {
        return 0;
    }
    int4 size = op->getIn(2)->getSize();
    Address addr(data.getArch()->getStackSpace(), uint32_t(stackOffset));
    data.newVarnodeOut(size, addr, op);
    op->getOut()->setStackStore();    // Mark as originally coming from CPUI_STORE
    data.opRemoveInput(op, 1);
    data.opRemoveInput(op, 0);
    data.opSetOpcode(op, CPUI_COPY);
    return 1;
}
class RuleVmpStoreVarnode : public Rule {
public:
    RuleVmpStoreVarnode(const string& g) : Rule(g, 0, "vmpstorevarnode") {} ///< Constructor
    virtual Rule* clone(const ActionGroupList& grouplist) const {
        if (!grouplist.contains(getGroup())) return (Rule*)0;
        return new RuleVmpStoreVarnode(getGroup());
    }
    virtual void getOpList(vector<uint4>& oplist) const;
    virtual int4 applyOp(PcodeOp* op, Funcdata& data);
};
void RuleVmpStoreVarnode::getOpList(vector<uint4>& oplist) const
{
    oplist.push_back(CPUI_STORE);
}
int4 RuleVmpStoreVarnode::applyOp(PcodeOp* op, Funcdata& data)
{
    VmpStackEvaluator evalCall;
    Varnode* offvn = op->getIn(1);
    int stackOffset = 0x0;
    if (!evalCall.EvaluateStackOffset(data, offvn, stackOffset)) {
        return 0;
    }
    int4 size = op->getIn(2)->getSize();
    Address addr(data.getArch()->getStackSpace(), uint32_t(stackOffset));
    data.newVarnodeOut(size, addr, op);
    op->getOut()->setStackStore();    // Mark as originally coming from CPUI_STORE
    data.opRemoveInput(op, 1);
    data.opRemoveInput(op, 0);
    data.opSetOpcode(op, CPUI_COPY);
    return 1;
}
class VmpStackEvaluator
 {
 public:
     bool EvaluateStackOffset(Funcdata& data,Varnode* vn,int& outOffset);
 private:
     uintb traceVarnodeStack(Varnode* vn);
     uintb tracePcodeStack(PcodeOp* op);
     uintb eval(PcodeOp* op);
 private:
     bool bContainEsp = false;
     bool bError = false;
     VarnodeData espLoc;
 };
 
uintb VmpStackEvaluator::eval(PcodeOp* op)
{
   if (bError) {
        return 0x0;
    }
    uintb val1 = traceVarnodeStack(op->getIn(0));
    uintb val2 = traceVarnodeStack(op->getIn(1));
    uintb calVal = op->getOpcode()->evaluateBinary(op->getOut()->getSize(), op->getIn(0)->getSize(), val1, val2);
    return calVal;
}
 
 
uintb VmpStackEvaluator::tracePcodeStack(PcodeOp* op)
{
    if (bError) {
        return 0x0;
    }
    OpCode code = op->code();
    switch (code) {
    case CPUI_INT_ADD:
    case CPUI_INT_SUB:
    case CPUI_PIECE:
    case CPUI_SUBPIECE:
        return eval(op);
    case CPUI_COPY:
        return traceVarnodeStack(op->getIn(0));
    default:
        bError = true;
        break;
    }
    return 0x0;
}
 
uintb VmpStackEvaluator::traceVarnodeStack(Varnode* vn)
{
    if (bError) {
        return 0x0;
    }
    if (vn->isConstant()) {
        return vn->getOffset();
    }
    if (vn->isInput()) {
        if (vn->getSpace() == espLoc.space && vn->getOffset() == espLoc.offset) {
            bContainEsp = true;
        }
        else {
            bError = true;
        }
        return 0x0;
    }
    PcodeOp* defOp = vn->getDef();
    if (!defOp) {
        bError = true;
        return 0x0;
    }
    return tracePcodeStack(defOp);
}
 
bool VmpStackEvaluator::EvaluateStackOffset(Funcdata& data, Varnode* vn,int& outOffset)
{
    espLoc = data.getArch()->translate->getRegister("ESP");
    if (!vn->isWritten()) {
        return false;
    }
    PcodeOp* defOp = vn->getDef();
    if (defOp->code() != CPUI_INT_ADD && defOp->code() != CPUI_INT_SUB) {
        return false;
    }
    outOffset = tracePcodeStack(defOp);
    if (bError) {
        return false;
    }
    return bContainEsp;
}
class VmpStackEvaluator
 {
 public:
     bool EvaluateStackOffset(Funcdata& data,Varnode* vn,int& outOffset);
 private:
     uintb traceVarnodeStack(Varnode* vn);
     uintb tracePcodeStack(PcodeOp* op);
     uintb eval(PcodeOp* op);
 private:
     bool bContainEsp = false;
     bool bError = false;
     VarnodeData espLoc;
 };
 

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2023-9-5 11:13 被fjqisba编辑 ,原因:
收藏
免费 3
支持
分享
最新回复 (2)
雪    币: 39
活跃值: (472)
能力值: ( LV9,RANK:155 )
在线值:
发帖
回帖
粉丝
2
牛逼牛逼,每个字都认识,连起来就看不懂了。
2023-9-5 10:53
0
雪    币: 4795
活跃值: (389632)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
可以,试了下你的插件,效果还不错。
2023-9-5 12:00
0
游客
登录 | 注册 方可回帖
返回
//