压在透明的玻璃上c-国产精品国产一级A片精品免费-国产精品视频网-成人黄网站18秘 免费看|www.tcsft.com

llvm的去平坦化

為了保護程序虛擬機是最常用的一種手段,并不是說用了多復雜的算法。主要是會花費大量的時間來把程序還原為本來的樣子。使用OLLVM項目中的控制流平坦化、虛假控制流、指令替換等混淆策略會進一步加大分析的難度。

Ollvm是什么

llvm是一個底層虛擬機,OLLVM(Obfuscator-LLVM)是瑞士西北應用科技大學安全實驗室于2010年6月份發起的一個項目,這個項目的目標是提供一個LLVM編譯套件的開源分支,能夠通過代碼混淆和防篡改,增加對逆向工程的難度,提供更高的軟件安全性。目前,OLLVM已經支持LLVM-4.0.1版本;所使用的編譯器是clang。

詳細介紹平坦化的介紹

llvm的安裝方法

llvm保護的大概方法是:程序主要使用一個主分發器來控制程序基本塊的執行流程進而模糊每個模塊之間的聯系。

源代碼如下:

#include <stdio.h>

int check(int a)
{
    if(a>0)
        return 3;
    else
        return 0;
}

int main()
{

    int a;
    scanf("%d",&a);
    if (check(a)==3)
    {
        puts("good");
    }
    else
    {
        puts("wrong");
    }
    return 0;
}

正常編譯如下

圖片.png

使用llvm編譯

./build/bin/clang main.c -o test -mllvm -fla

圖片.png

圖片.png

程序流程圖明顯復雜很多。

看一下check函數

原程序

signed __int64 __fastcall check(__int64 a1)
{
  char v1; // bl@2
  signed __int64 result; // rax@8
  int i; // [sp+1Ch] [bp-14h]@1

  srand(0x64u);
  for ( i = 0; i < strlen((const char *)a1); ++i )
  {
    v1 = *(_BYTE *)(i + a1);
    *(_BYTE *)(i + a1) = v1 + rand() % 5;
  }
  if ( *(_BYTE *)a1 != 97 || *(_BYTE *)(a1 + 1) != 98 || *(_BYTE *)(a1 + 2) != 99 || *(_BYTE *)(a1 + 3) != 100 )
    result = 0LL;
  else
    result = 4LL;
  return result;
}

平坦化程序

__int64 __fastcall check(__int64 a1)
{
  size_t v1; // rax@13
  signed int v2; // ecx@13
  char v3; // ST04_1@16
  signed int v4; // eax@18
  signed int v5; // eax@21
  signed int v6; // eax@24
  signed int v7; // eax@27
  signed int v9; // [sp+44h] [bp-1Ch]@1
  int v10; // [sp+48h] [bp-18h]@1
  signed int v11; // [sp+5Ch] [bp-4h]@0

  srand(0x64u);
  v10 = 0;
  v9 = 706310565;
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
          while ( 1 )
          {
            while ( 1 )
            {
              while ( 1 )
              {
                while ( 1 )
                {
                  while ( v9 == -2109444161 )
                  {
                    v7 = 1322041670;
                    if ( *(_BYTE *)(a1 + 3) == 100 )
                      v7 = 867560817;
                    v9 = v7;
                  }
                  if ( v9 != -2069803162 )
                    break;
                  ++v10;
                  v9 = 706310565;
                }
                if ( v9 != 306556692 )
                  break;
                v4 = 1322041670;
                if ( *(_BYTE *)a1 == 97 )
                  v4 = 564228819;
                v9 = v4;
              }
              if ( v9 != 341947172 )
                break;
              v6 = 1322041670;
              if ( *(_BYTE *)(a1 + 2) == 99 )
                v6 = -2109444161;
              v9 = v6;
            }
            if ( v9 != 564228819 )
              break;
            v5 = 1322041670;
            if ( *(_BYTE *)(a1 + 1) == 98 )
              v5 = 341947172;
            v9 = v5;
          }
          if ( v9 != 682671478 )
            break;
          v3 = *(_BYTE *)(a1 + v10);
          *(_BYTE *)(a1 + v10) = rand() % 5 + v3 - 97 + 97;
          v9 = -2069803162;
        }
        if ( v9 != 706310565 )
          break;
        v1 = strlen((const char *)a1);
        v2 = 306556692;
        if ( v10 < v1 )
          v2 = 682671478;
        v9 = v2;
      }
      if ( v9 != 867560817 )
        break;
      v11 = 4;
      v9 = 1000770411;
    }
    if ( v9 == 1000770411 )
      break;
    if ( v9 == 1322041670 )
    {
      v11 = 0;
      v9 = 1000770411;
    }
  }
  return (unsigned int)v11;
}

如果分析下來,難度會比較大。

嘗試進行函數的恢復

按照如下思路進行去平坦化:

  1. 函數的開始地址為序言的地址
  2. 序言的后繼為主分發器
  3. 后繼為主分發器的塊為預處理器
  4. 后繼為預處理器的塊為真實塊
  5. 無后繼的塊為retn塊
  6. 剩下的為無用塊

獲取相關的塊列表地址,就能夠通過angr定義規則,來約束函數模塊。

(按照miasm功能介紹,可以獲取反編譯后的模塊地址,但因為報錯太多就放棄了。)

之后便找到了bird大佬寫的腳本,將上面報錯部分修改之后拿來用。

程序恢復結果:

圖片.png

圖片.png

圖片.png

2018 X-NUCA 使用腳本去平坦化

初始

圖片.png

圖片.png

恢復之后

圖片.png

但程序還使用了”指令替換” -subv 里面還有指令替換,好在程序的邏輯并不復雜。

第一部分比較復雜:

圖片.png

第二部分就比較清晰了:

圖片.png

根據后面的來觀察執行的操作,函數加密分為兩部分。每部分各16個字符,后面16個進行的是三次異或運算。所有運算的結果都儲存在6130D0中,動態調試的過程中要時刻注意其中的結果。(取巧的方法:因為我們可以確定flag的前七位是X-NUCA{ ,通過結果計算,這樣會更容易找到加密的位置)

str = '012345abcdefghijklmnopqrstuvwxyz'
a=[0x68,0x1C,0x7C,0x66,0x77,0x74,0x1A,0x57,0x06,0x53,0x52,0x53,0x02,0x5D,0x0C,0x5D]
b=[0x04,0x74,0x46,0x0E,0x49,0x06,0x3D,0x72,0x73,0x76,0x27,0x74,0x25,0x78,0x79,0x30]
flag1=""
flag2=""
j=0
for i in range(16):
    flag1+=chr(a[i]^ord(str[i]))
for i in range(16):
    j=i+16
    flag2+=chr(b[i]^ord(str[j])^a[i]^ord(str[i]))
print flag1+flag2

總結:符號執行對于虛擬機的分析作用很大,且二進制分析工具了解太少,如果沒有bird 大佬的腳本,現在也不能成功去平坦成功一次。而且也看到了,對于指令替換便需要重新寫一新的腳本。 miasm的功能很強大,應當盡快掌握。

文章文件鏈接:https://pan.baidu.com/s/1CEXUZ-Yb_t__3770WL51cw

提取碼:a0wt

文章參考:

bird@tsrc?https://paper.seebug.org/192/

miasm

Deobfuscation: recovering an OLLVM-protected program

原文鏈接:https://www.anquanke.com/post/id/169318

上一篇:SSHazam:如何利用SSH隧道實現隱蔽C2通信

下一篇:“安全+”沙龍第十四期在上海成功舉辦/1月4日