控制流迷惑——控制流整平

一、控制流整平的作用
    學逆向的人都知道,if-else、while、for具有典型的跳轉等結構,即使通過多層嵌套、拓展條件等方法,依然可以通過“切片技術”來判斷。有了這些依據,就給程序分析帶來很多便利。
    正是這個原因,爲了增加程序逆向的難度,我們得使得這些特徵結構變得模糊,並且能讓類似“切片技術”這樣基於具體語義分析的方法失效,迫使逆向分析人員進行完整的抽象語義分析,斬斷所謂的“捷徑”。
    控制流整平的策略是這樣的,它把所有的典型控制流以及其衍生結構“統而爲一”,各種控制流的區別只是語義方面的,增加了理解控制流轉換關係的難度。

二、什麼是控制流整平
    控制流整平迷惑,是通過打破程序原有的控制流之間的嵌套和順序關係,使得變換後的程序控制流扁平化的混淆方法,其基本思想是令程序中所有的基本塊擁有共同的前驅和後繼代碼塊。
如下圖(本文代碼思路皆使用C語言表示)

     進行控制流整平後,使得面向過程的代碼片段,原來比較清晰的控制流向混雜在一起,同時這也比較好的並行圖形態,也有利於進一步的迷惑處理。

三、順序流整平
    對於單純的順序流,一般不用控制流平整的方法,我們有更好的處理方法,但這裏爲了從基礎一步一步講控制流平整,所以從最簡單的順序流開始。
舉例:
int main()
{
  int a,b,c;
Step1:
    a=0;
Step2:
    b=1;
Step3:
c=2
}
轉換爲:
int main()
{
  int a,b,c;
  int i=0
L1:
  switch(i)
 {
  case 0:
     a=0
     i=1;
     goto L1;
  case 1:
     b=1
     i=2;
     goto L1;
  case 2
     c=3;
     i=3;
     goto L1;
  default:
   NULL;
 }
}

四、條件流平整
    相信你看完順序流平整的代碼後,條件流平整應該也能“依葫蘆畫瓢”的寫出來,唯一的問題就是條件控制流經常會有嵌套的問題,還有不同條件如何套在switch裏。
例如:
int main()
{
 int a,b;
 bool b1=xxx,b2=xxx;
 if(b1)
 {
   a=1;
   if(b2)
     b=1;
   else
     b=0;
 }
轉換爲
int main()
{
 int a,b;
 bool b1=xxx,b2=xxx;
 int i=b1
L1: 
 switch(i)
 {
  case True:
     a=1;
     i=b2+2;
     goto L1;
  case True+2:
    b=1;
    goto L1;
  case Flase+2:
    b=0;
    goto L1
    case Flase:
    a=0;
    goto L1;
 }
}

五、循環控制流迷惑
    循環控制流也可以被當做條件控制流一樣轉換,即循環和不循環的條件分支。但是爲了取得更好的迷惑效果,單次循環內的順序流也可以進行控制流整平,以增強迷惑效果。

六、分支變量保護
    對於上面的方法面臨的一個問題是:“分支變量值的如何保護?”。如果分支變量的值能夠很容易地被分析出來,則程序可能被反迷惑成原程序,從而達不到代碼迷惑的效果,下面提出幾種可行的方案,供大家參考。
方法一:switch(i) 轉換爲 switch(f[i]),這樣控制流向需要“即時計算”來確定,其實就是Hash。

方法二:演示代碼中,各分支只進入一次,但可以使某些代碼塊多次重複進入,當然要考慮效率問題。

方法三:演示代碼只有一層,如果多層嵌套,並且把同一層的代碼塊放入各層的不同深度,但其付出的空間和時間成本是一個值得注意的問題,並且其最大的缺點是後續添加其他迷惑技巧時可能受到限制。

方法四:方法三可以認爲是縱向拓展,那麼橫向用多個switch之間的控制流連接,雖然它的複雜度比方法二小,但是繼續添加迷惑技巧時受限較小。因爲如果把方法一提到的“即時計算”部分挪到並行的switch裏,這樣前一個switch看起來都像正常的switch+break的形態。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章