脫殼_01_虛擬機殼_VMP


寫在前面的話:

上一篇文章中,帶領大家一起分析了簡單的壓縮殼ASPACK,今天,就和大家一起來揭開VMP這道神秘的面紗;

【花指令】:擾亂調試器的,並不執行;

【混淆】:對原指令進行拆解或等價替換,會執行;

零、自己寫個程序,加殼

 0、為方便我們對VMP殼有更清楚的認識,這里,我們先自己寫個程序,然后,加殼;

通過分析對比壓縮前后的程序,輔助大家進行更深一層的了解,測試代碼如下:

#include <windows.h>

int main(int argc, char** argv) {
    MessageBox(0, L"VMP Simple Example", L"Reginald", 0);

    ExitProcess(0);

    return 0;
}

1、加殼

已經生成了,這里是問,要不要運行,點擊YES,會運行我們加殼后的程序,我們看一下加殼后的程序:

加殼完成,下面,我們一起來分析下加殼后的程序;

 一、分析加殼后的程序:

在我們進行脫殼時,如果事先不知道這是什么種類的殼,方法只能是單步跟蹤了;

這里,在分析前,我們可以從宏觀上看下,OEP處有何變化;

加密后的OEP;

加密前的OEP(VS編譯的OEP特征 call xxx; jmp yyy)

因為在上面加殼的時候,我們選擇了對OEP處的第一條指令加保護,所以,只有第一條指令發生了變化;

我們單步跟蹤下這部分的代碼:

遇到call,F7跟進去;

我們來看下:

1、保存寄存器環境;

2、將0xB50000 壓棧;

3、ESI = *(ESP + 0x2C);

4、EBP = ESP,ESP -= 0xC0;開辟棧幀

5、EDI = ESP,EDI指向新棧頂

6、ESI += *(EBP);ESI內容加上原棧頂內容——我們一會動態分析下;

      EAX = *(ESI);

      ESI++;

7、跳轉到*(EAX*4 + 0xF661E9);

目前,好像就是在做這些操作,我們貌似看不出什么了,但這里不免有幾個疑問:

A) 0xB50000 有什么含義;

B) 最后跳轉的時候,那個0xF661E9又有什么含義;

我們帶着疑問繼續動態分析:

目前,我們已經了解到了一些有用的信息:

1、EBP保存原始棧頂

2、EDI保存新棧頂(老棧頂 - 0xC0)

3、ESI處經過運算,將兩個無意義的值,變化為了一個有效的地址值【距離VMP段首偏移0x78A處】

4、EAX從ESI處取值,每次1B;

5、進行跳轉時,加的常數也是位於VMP區段的地址值【距離VMP段首偏移0x1E9處】

那我們就在010里瞅瞅,0x78A和0x1E9里到底有什么東西;(VMP段內偏移值)

到現在,我們可以做出如下假設:

0x1E9處相當於函數表首地址;

0x78A處取出下標,根據下標進行定位,找出該地址,跳轉到此地址;

這些地址,也都位於VMP段,這也符合情況,稍后會給出解釋;(什么叫符合情況);
這些地址,都是需要重定位的;

繼續分析,驗證假設:

 

我們在010里,0x1E9開始,找出0x1C個地址(地址4B):

0xF50000 + (0x4160B0 - 0x400000) = 0xF660B0(重定位后的地址),那我們要跳轉的地址,是這個嗎,在OD里走下:

接下來,我們繼續分析:

 為了方便我們的分析,希望大家將下面的棧圖放在心中:

我們已經大致清楚了這部分的邏輯,那我們先把那些用到的下標,找出來,並且找出它所對應的地址,靜態的先分析下:

1C 14 30 38 10 00 0C 34 18 3C 08 28
1C:0xF660B0
14:0xF660B0
30:0xF660B0
38:0xF660B0
10:0xF660B0
00:0xF660B0
0C:0xF660B0
34:0xF660B0
18:0xF660B0
3C:0xF660B0
08:0xF660B0
28:0xF660B0

這些都一樣,我們瞅瞅是干嘛的:

0xF660B0:【ESP、EDI不變,*(EDI + EAX) = *EBP,EBP += 4】

執行完后:【ESP、EDI不變,EBP += 0x30】

再接着看下面的:

62 60 12 40 00

62:0xF666C0


【EBP -= 4,*EBP = 0x401260(原ESP + 0x2C = 0x401260)
【EBP VS (EDI + 0x50)】
【(原ESP + 0x2C) > (原ESP - 0x70)】end

這里,要注意這個地方:

1、原ESP + 0x2C,這個位置還記得么,就是最初進入VM的時候,push xxx; call yyy的那個push處的地方;

2、這個地址,0x401260,看着這么親民,有什么意義嗎;

這里,為了弄清楚這個地址的含義,我們先看看未VM保護前的程序,其實,這里不得不說單純VM的缺陷了,

它只是在做特別厲害的混淆,但是,不論你怎么混淆,該做的,一定要做的,否則,程序就錯了;

那它應該做什么呢?還記得,我們加VM保護的指令嗎?

OEP:CALL xxx

OEP+5:jmp yyy

CALL xxx = push(OEP+5) && jmp xxx;

我們即使加了VM保護,但是,總要執行xxx處的代碼吧,執行完后,又一定會回到OEP+5那吧;

我們如何得到xxx的地址呢,別慌,我們看下機器碼,知道OEP的地址了,又有E8后的偏移,不是問題:

這是原來的OEP處的數據,E8 AD 03 00 00

第一:0xF5125B + 0x3AD + 5 = F5160D(執行函數)
第二:0xF5125B + 5 = F51260(返回地址)

到這一步,我們回過頭來,再看看剛剛那個親民的地址:0x401260

這么有感覺吧,像不像一個沒有被污染(重定位)的VA,如果是的話,Offset就是0x1260;

再看看那個返回地址0xF51260-0xF50000(當前基址)=0x1260,因此,基本確定,這里就是在搞事情了;

把執行完原該執行的函數后,返回的地址,放到了原ESP+0x2C的位置;一切構造的是那么巧妙,現在知道為啥那個位置要來個push了吧,真是一箭雙雕;

思路清晰的讀者,也許心中還有一個疑問,別忙,接下來就幫你解答:

1E 2B 04 1E
1E:0xF66757

【0xFF660B0逆操作,EDI、ESP不變,EBP -= 4,*EBP = *(EDI + EAX)】
【每次EBP向反方向變化,都要進行如下比較】
【EBP VS EDI + 0x50】
【原ESP + 0x28 > 原ESP - 0x70】end

2B:0xF6619B(神來之筆)

【*(EBP + 4) += *EBP; *EBP = EFLAGS; EDI不變,ESP不變, EBP不變,前倆內容有變化】

這是在干嘛呢,我們一步步分析到這里的時候:

EBP = 原ESP + 0x28; 

*(原ESP+0x2C) = 0x401260(未受污染的返回地址OEP+5);

解釋下,就是在執行:

*(ESP+0x2C) += *(EBP)

EBP的值哪里來的,就是上一步過來的,EAX=0x1E 時的那步逆操作,注意EAX & 0x3C后和EDI相加的,也就是0x1C

【0xFF660B0逆操作,EDI、ESP不變,EBP -= 4,*EBP = *(EDI + EAX)】

由於EDI一直未有變化,我們只需要找到什么時候往EDI+0x1C里存東西,就可以了,就是最初的時候:那個1C:

最初的時候,EBP就是原棧頂,1C的時候,執行*(EDI+0x1C) = *EBP,其實就是把當時棧頂的數,放進去了,那當時棧頂的數是什么呢;說出來,你會驚呼它的神奇:

還記得當時,我們認定無效的兩個數么,一個是push xxx; call yyy進入虛擬機的;一個是最后push的那個0xB50000

也就是說,當時棧頂的數據,是當前基址-默認基址,現在那些有疑問的朋友,也許如撥雲見日了吧;

*(原ESP+0x2C) = 0x401260(未受污染的返回地址OEP+5);

*(原ESP+0x2C) += 0xB50000;(進行重定位)

04:0xF660B0

1E:0xF66757 由於兩個對EBP而言是互逆的,因此,不再分析了;EBP還是原ESP+0x28

接下來,我們繼續分析,還有最后一處讓人驚艷的(加法結合律)

D7 0D 16 40 00
D7:0xF666C0
【EBP -= 4,*EBP = 0x40160D 原ESP + 0x24 = 0x40160D
【EBP VS EDI + 0x50】
【原ESP + 0x24 > 原ESP - 0x70】end

這個套路,我們已經分析過了,繼續走:

4A:0xF6619B

我們已經知道,這是修復重定位的,但是,現在存放該執行函數地址的,是原ESP+0x24的位置,返回地址位置是ESP+0x2C;

烏雲啊,是我們分析錯了嗎,別急,我們也許忽略了一些常識,人最容易忽視的,其實是司空見慣的東西;自認為常識的東西;

重新審視修復重定位的函數:

它執行的操作是什么呢:

*(EBP+4) += *(EBP)

*EBP = EFLAGS;

現在*EBP存放的是0x40160D(有意義的地址);那*(EBP+4)里是啥呢,剛剛在分析上一個重定位的時候,已經知道了,這個位置,是(當前基址 - 默認基址) = 0xB50000

哦,原來如此,這不就是一個結合律的問題嗎,誰先加誰的問題,一個主動的,一個被動的,我們把目光都放在了那個更有意義的點了,忽略了另外的也是有意義的;

至此,我們得出結論:

原ESP+0x2C <=> 應該返回的位置;

原ESP+0x28 <=> 應該執行的位置;

分析至此,可以欣慰以下了,接下來,繼續走,既然准備工作基本完成,猜測,該退出VM了;

04 3E 1A 36 0E 02 12 3A 32 16 1E 

04:0xF660B0

3E:0xF66757 互逆

1A:0xF66757
【原ESP + 0x20 > 原ESP - 0x70】end
36:0xF66757
【原ESP + 0x1C > 原ESP - 0x70】end
0E:0xF66757
【原ESP + 0x18 > 原ESP - 0x70】end
02:0xF66757
【原ESP + 0x14 > 原ESP - 0x70】end
12:0xF66757
【原ESP + 0x10 > 原ESP - 0x70】end
3A:0xF66757
【原ESP + 0xC > 原ESP - 0x70】end
32:0xF66757
【原ESP + 0x8 > 原ESP - 0x70】end
16:0xF66757
【原ESP + 0x4 > 原ESP - 0x70】end
1E:0xF66757
【原ESP > 原ESP - 0x70】end

到這里,EBP就是原ESP了,見到曙光了,再接着走:

3B
3B:0xF66091

POP次,ESP的內容剛剛好,0x28(該執行的位置) && 0x2C(該返回的位置)

 如此,我們靜態分析了調用過程,為了驗證猜想,直接再這個地方下斷,看下棧結構:

 二、總結:

0、單純的VMP殼,只是在混淆代碼(不妨試試,在IAT地址處搞一搞,就清楚了)

1、最初push xxx; call yyy; 這個push一方面和另外的基址之差定位到下標;另一方面,填充一個棧位(其實就是占位的),最終會被call 指令本應push的地址給替換;

2、進入里面后,最后一個push的值,是基址之差,用來修復call重定位的

3、VMP里,大多是混淆后,變着法的操作原堆棧,在操作完成后,就退出了;

4、退出OEP特征碼:89 EC ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? C3

5、其實,經過這方分析后,感覺特征仍有不少,另外,靜態分析,也變成了可能;知道VMP段的RVA后 x4 00,也能幫助定位;

6、對於CALL指令,VM保護,就是如上的,也可以分析別的指令,了解其處理方法,總體而言,單純的VM,仍然離VM有些距離;

希望,能有所幫助;

轉載請注明出處,TKS;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM