phi指令在控制流平坦化的修复

admin 2024年12月10日17:54:14评论17 views字数 2382阅读7分56秒阅读模式
phi指令在控制流平坦化的修复

这里填充错了,后面只能改20个字,😓,统称为科学技术,简称科技。实际二者既有密切联系,又有重要区别。科学解决理论问题,技术解决实际问题。

phi指令在控制流平坦化的修复
phi指令在控制流平坦化的修复

⊙1.修复原因

2.修复过程

⊙3.注意点

1.修复原因

在控制流平坦化结束后,ollvm调用了fixstack, fixstack中对phi-node和demote reg,移入了stack,本文只分析phi-node指令to stack的过程。

先说为啥要处理phi指令,在控制流平坦化后,执行真正逻辑的基本块的前驱块大概率变为分发块,或者其他的前驱块,根本上改变了phi指令能够正常执行的逻辑。

我们先来看一下phi指令:

inttest(int a, int b) {if (a < b) {return a + 1;    } else {return b + 1;    }}

经过转为ir后为:

define i32 @test(i32 %a, i32 %b) {entry:  %cmp = icmp slt i32 %a, %b  br i1 %cmp, label %if_true, label %if_falseif_true:  %result1 = add i32 %a, 1  br label %mergeif_false:  %result2 = add i32 %b, 1  br label %mergemerge:  %result = phi i32 [ %result1, %if_true ], [ %result2, %if_false ]  ret i32 %result}

也就是说第12行的result的值,会根据从%if_true块或%if_false块的不同,从而得到%result1、%result2的返回值,所以对于merge来说,从那个快跳转来的极为重要。

2.修复过程

如果将上方的代码经过了flatting后,那么到merge块的前驱块就不为原始的块了,所以代码出现了混乱,无法正常原有的逻辑被实现。

那么有一个方案(这个是看了ollvm源码才得来的方案):因为本质上phi指令的值是根据前驱块来源然后得来的,可以先在入口块alloca,在原phi的前驱块里面进行store,然后在phi指令用的那个时候,把value从栈中load出来,在这种情况下完美替换了phi指令,这个llvm已经给实现了。

根据llvm DemotePHIStack(参见llvm源码)画出了程序流图如图所示:

phi指令在控制流平坦化的修复

其对应的代码如下所示

AllocaInst *llvm::DemotePHIToStack(PHINode *P, Instruction *AllocaPoint){if (P->use_empty()) {    P->eraseFromParent();returnnullptr;  }const DataLayout &DL = P->getModule()->getDataLayout();// Create a stack slot to hold the value.  AllocaInst *Slot;  if (AllocaPoint) {    Slot = new AllocaInst(P->getType(), DL.getAllocaAddrSpace(), nullptr,                          P->getName()+".reg2mem", AllocaPoint);  } else {    Function *F = P->getParent()->getParent();    Slot = new AllocaInst(P->getType(), DL.getAllocaAddrSpace(), nullptr,                          P->getName() + ".reg2mem",                          &F->getEntryBlock().front());  }  // Iterate over each operand inserting a store in each predecessor.  for (unsigned i = 0, e = P->getNumIncomingValues(); i < e; ++i) {    if (InvokeInst *II = dyn_cast<InvokeInst>(P->getIncomingValue(i))) {      assert(II->getParent() != P->getIncomingBlock(i) &&             "Invoke edge not supported yet"); (void)II;    }    new StoreInst(P->getIncomingValue(i), Slot,                  P->getIncomingBlock(i)->getTerminator());  }  // Insert a load in place of the PHI and replace all uses.  BasicBlock::iterator InsertPt = P->getIterator();  for (; isa<PHINode>(InsertPt) || InsertPt->isEHPad(); ++InsertPt)    /* empty */;   // Don't insert before PHI nodes or landingpad instrs.  Value *V =      new LoadInst(P->getType(), Slot, P->getName() + ".reload", &*InsertPt);  P->replaceAllUsesWith(V);  // Delete PHI.  P->eraseFromParent();  return Slot;}

可以几张图来对比下经过phi to stack指令后的对比图:分别为alloca,store,load

phi指令在控制流平坦化的修复

phi指令在控制流平坦化的修复

phi指令在控制流平坦化的修复

3.注意点

在处理store和load指令的时候,要注意不要对于异常处理的指令前进行插入。

学习逆向和爬虫可以关注我朋友:

我是BestToYou,分享工作或日常学习中关于Android、iOS逆向及安全防护的一些思路和一些自己闲暇时刻调试的一些程序,文中若有错误或者不足的地方,恳请大家联系我批评指正。

原文始发于微信公众号(二进制科学):phi指令在控制流平坦化的修复

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年12月10日17:54:14
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   phi指令在控制流平坦化的修复http://cn-sec.com/archives/3491739.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息