我最近听说“控制流扁平化”混淆,它似乎是用来破坏二进制程序CFG的结构的(请参阅Diablo混淆模块和符号执行和CFG扁平化)。

有人可以解释其基本原理是什么,以及如何产生这种混淆(工具,编程技术等)吗?并且,很高兴知道是否有方法可以提取程序控制流的真实形状。

评论

我认为这份inf.u-szeged.hu/~akiss/pub/pdf/laszlo_obfuscating.pdf文件完全说明了您提到的实施技术

@nomilk:太好了,我不知道这篇论文。非常感谢。

别提了:)

#1 楼

有关这种混淆的一个很好的示例,请查看Apple的FairPlay代码,例如iTunes或iOS库。这是应用了这种混淆处理的函数的典型图形:使用新的人工变量来决定应跳至下一个块的调度程序节点。在每个单独的基本块的末尾更新此变量。

这是调度程序节点: 。

这是基本块之一:

LDR    R3, =0xF26A85D2
ADD    R3, R2, R3
CMP    R3, #0x40 ; switch 65 cases
ADDLS  PC, PC, R3,LSL#2 ; switch jump


它将更新R2的值,该值将用于跳转到下一块。

在大多数情况下,恢复它应该不太困难-只需跟踪控制变量更新,并用到新控制变量值对应的下一个块的跳转替换对调度程序节点的跳转即可。 />

评论


如何在不更改实际二进制文件的情况下编辑部件?

–heinrich5991
2013年6月10日20:00

@ heinrich5991在IDA中,我们可以更改所分析的二进制文件的程序集表示形式,而无需更改实际的二进制文件,然后可以在IDA中保存此表示形式以供以后使用。

– nomilk
2013年6月10日20:08

#2 楼

来自Timea Laszlo和Akos Kiss的这篇论文:

实现函数的基本方法如下:
首先,将函数的主体分解为基本块,然后将其分解为基本块将所有原先处于不同嵌套级别的所有这些块彼此相邻,然后将它们放置在一起。
现在将相等级别的基本块封装在一个选择性结构中(一个开关C ++语言中的语句),并在单独的情况下使用
每个块,然后将选择封装在循环中。
最后,通过表示程序状态的控制变量,该变量在每个基本块的末尾设置,并用于封闭循环和选择的谓词。

Image showing how control-flow flattening obfuscation alters code that contains loop structures.
A simple example:
int original()
{
    print "Do"
    print "you"
    print "like"
    print "milk?"
}


int obfuscated()
{
    int ctrFlowVar = 1;

    while(ctrFlowVar != 0)
    {
        switch(ctrFlowVar)
        {
            case 1:
                print "do"
                ctrFlowVar = 2;
                break;
            
            case 2:
                print "you"
                ctrFlowVar = 3;
                break;
            
            case 3:
                print "like"
                ctrFlowVar = 4;
                break;
            
            case 4:
                print "milk?"
                ctrFlowVar = 0;
                break;
        }
    }
}

如果您熟悉用switch编写assembly语句的方式(我知道两种方式,if样式和跳转表),那么上面的示例很简单要消除混淆。break;指令是jmp。您可以使其跳转到下一个应该是的块。