【轉】脫殼中的附加數據問題(overlay)

發佈人:kgdyga

1.前言
最近,在論壇上看到很多人在弄附加數據overlay的問題,加上上次答應了各位兄弟所以覺得寫一些着方面的廢話。如果下面的內容對你有幫助那是最好。
這篇文章我們將解決以下問題:
1.什麼是overlay,怎麼找到overlay?
2.爲什麼有些殼雖然有overlay但是卻不用特別處理?
3.爲什麼有些殼只用粘貼overlay數據就ok了,而有些殼卻要定位指針?
4.如何修復文件指針?
--------------------------------------------------
2.正文
一.什麼是附加數據(overlay)
1.實際當中的overlay
其實,overlay雖然大家在脫殼當中覺得很陌生,但是他離我們並不遙遠。在我們平時使用的軟件當中,有一些軟件要處理一些數據流文件,比如winamp。當我們下載了mp3文件(數據文件),沒有播放器是不可能播放的,與此相關的還有很多,比如txt文件和notepad的關係也差不多。而這些數據文件被單獨的保存在硬盤上,當我們使用notepad的打開功能的時候,就可以去讀取數據文件裏面的東西了。
overlay又是什麼意思呢?他其實真正的意思就是取消打開功能,將這些需要讀取的數據放到pe文件的後面,讓程序自動的運行打開的功能。這樣的功能就變成了一個notepad的程序對應只能打開一個文件。
最典型的就是一些軟件可以把一些數據流文件生成exe文件,比如一些mp3生成器,flash生成器,以及我們用來做動畫的S-demo。他們的作用就是將數據對pe進行捆綁。(這樣做的結果也就是爲什麼我們對這些文件用UPX等pe壓縮工具卻不能壓縮他的原因,這是後話了)

2.技術上的overlay
在我們對pe文件的overlay進行分析之前,我們要普及一下文件映射的知識。
在pe裏面,有所謂的文件偏移RA,文件偏移大小RS和與其對應的虛擬地址偏移VA,虛擬地址偏移大小VS。
我們要深刻的理解以上的概念不是我這篇文章能說清楚的,但又是搞overlay必須得弄清楚的,於是我簡單的說明一下。
在我們的磁盤上的pe文件裏面,排列着的數據在運行的時候將被影射到內存空間。他們將被怎麼影射呢?舉個例子:
例1:
一個pe文件中只有兩個區段(pe head不算)
第一個區段是.text VA=401000 VS=1000 RA=200 RS=100
第二個區段是.data VA=402000 VS=1000 RA=300 RS=100
假設我們打開winhex看到在文件偏移300處的情況是這樣的:
RA=2FF 處的數據是12 RA=300處的數據是34

好了,現在當pe文件裝到內存中,那麼會出現什麼結果呢。
1.文件偏移中的200開始的100個字節將被影射到內存的401000開始的100個字節,後面的F000個字節將用0填充。
2.文件偏移中的300開始的100個字節將被影射到內存的402000開始的100個字節,後面的F000個字節將用0填充。

也就是說,在磁盤是上相鄰的兩個數據12和34,在內存空間中將分開得老遠,12在40102FF處,而34卻在402000處。

當然這只是我自己隨便舉的一個極端的例子,實際上由於文件的對齊機制在磁盤上每個段的結束都是填充了大量的0

爲什麼要講這些呢?

因爲區段是一般將會被映射到內存的,如果上面的例子中。將.data區段去掉。那麼在磁盤上就將會留下從RA=300到RA=400其大小爲100的數據,不會被影射到內存中。而這部分數據就將被認爲是附加數據-overlay。

3.做個實驗
1.用lordPE的pe editor打開一個notepad
2.打開他的senctions看看
3.把他的最後一個區段的文件大小RS修改一下(改成F00吧,少影射100的文件)
4.用peid打開來看看吧。

PEID顯示:Microsoft Visual C++ 6.0 SPx Method 1 [Overlay]

說明什麼問題呢?
1.overlay只是數據他是不映射到內存的,他將被程序以打開自己的方式來讀取數據
2.只要不是區段裏面包括的文件的大小,將被視爲overlay

好了到這裏可以總結一下了:
1.附加數據是在附加在文件後面的,不被映射到內存空間中的數據,他提供他自己的程序打開自己來讀取,所以dump下來的時候是沒有overlay的,需要我們手動把這一部分的數據粘貼到dump下來的數據後面。
2.一般來說在區段裏面我們能找到所有區段的大小,這個大小的後面就是overlay的開始,於是對於上面的實驗來說,他的overlay的開始地方就是最後一個區段的RA+RS(就是C000+F00=CF00)大小是從CF00到D000的最後100。
3.現在可以回答一個簡單的問題了。爲什麼有些殼是overlay的,但是我們卻不用處理他。因爲他根本沒有讀取後面的數據,按照我們上面的實驗我們完全可以做另一個實驗就是在原來的notepad後面添加一個字節的00,而這時peid也會視爲是overlay。試問這樣的overlay難道我們也要複製他嗎?

二.如何解決附加數據的問題

win32程序對文件的操作有兩中普通的文件操作和內存鏡象文件,在這裏我們主要是討論普通的文件操作。

1.要回答這個問題首先就會關係到兩個函數CreateFileA和SetFilePointer。對於CreateFileA他是打開文件的函數,當你下斷CreateFileA你會發現程序斷下的時候,他的第一個參數就是這個pe文件在磁盤上的完全路徑,表示他正在對文件打開,將要對他進行文件讀寫操作。這個函數我就不多說了。下面詳細說說SetFilePointer

DWORD SetFilePointer(
HANDLE hFile, //在用CreateFileA打開後得到的文件句柄
LONG lDistanceToMove, //要移動的距離,這個是低32位
PLONG lpDistanceToMoveHigh, //要移動的距離,這是高32位,要注意這是一個指向數據的指針
DWORD dwMoveMethod //表示指針開始的位置
);


因爲用CreateFileA打開一個文件後,系統會給這個文件維護一個指針,一開始是指向文件的第一個字節的。這個函數的作用是人爲的移動這個文件指針。理由很簡單,overlay數據不是在文件的第一個字節處。而他的返回值就是移動後的文件的新指針的位置。
上面的有兩個參數要注意:

1.lpDistanceToMoveHigh這個和上面的lDistanceToMove不一樣,他是一個指針。指向一個32位的內存地址裏面放着高32位的移動距離,他和lDistanceToMove的低32位的距離合起來表示一共要移動的距離(感覺是多此一舉,一個32位就可以尋址4G,難道有一個4G的文件!?,而且高32位還設置成指針,難道是怕直接引用還不夠?MS真是考慮得長遠!)

2.dwMoveMethod有3個參數
FILE_BEGIN =0 表示指針不管當前的位置在什麼地方,從第一個字節開始算。
FILE_CURRENT =1 表示指針繼承上面的,最後移動到的位置
FILE_END =2 表示指針不管當前的位置在什麼地方,從最後一個字節開始算。

2.爲什麼dump下來的程序需要定位overlay?

dump的意思就是將內存的數據全部存儲到磁盤,也就說對於上面的那個例1的例子來說,當我們從磁盤再把他dump下來的時候並不是還是400的大小了,而是3000的大小了!這一點請務必弄清楚。這時在原來文件中在200位置的數據,在dump下來的文件中就在1000處,依次類推可以得到其他的文件位置,這也就是爲什麼我們在dump下來後需要將RA=VA RS=VS的原因,幸虧體貼的lordPE自動的完成了這一個步驟!

這時雖然我們把overlay的數據粘貼到脫殼後的程序,但是與原來的文件位置完全變了。導致我們脫殼以後要重新定位文件指針讓他讀取到正確的數據。

3.爲什麼有些程序粘帖了overlay的數據就ok了?

這個東西口說無憑讓我們來實戰一下吧。就用上次pendan2001兄弟給的做例子吧。

http://nj2.onlinedown.net:81/files/zmgb2.0.rar

在脫完殼以後,修復輸入表。發現運行不了。呵呵~沒有填加附加數據嘛,不然我舉這個例子幹什麼。

看他的區段發現他的最後一個區段是8E00開始,大小是1000。所以我們估計他的附加數據是從8E00+1000=9E00開始的地方。

用winhex打開未脫殼文件,到9E00處吧。呵呵~發現上面有很多0,這就是爲什麼有些牛人告訴我們找前面是0的原因,其實其本質是因爲文件的對齊機制。

複製從這裏到後面全部的數據到修復好輸入表的那個(爲什麼是要先修復輸入表,等下你就知道了^^)

好了,可以運行了。不明白爲什麼是嗎?

到程序裏面看看吧

用od載入,下斷bp CreateFileA 和bp SetFilePointer F9以後斷下了,看堆棧

0012FC70 00401183 /CALL 到 CreateFileA 來自 桌面鋼筆.0040117D
0012FC74 0012FDA0 |FileName = "G:/downloads/桌面鋼筆V2.0.exe" //開始打開文件了
0012FC78 80000000 |Access = GENERIC_READ
0012FC7C 00000001 |ShareMode = FILE_SHARE_READ
0012FC80 00000000 |pSecurity = NULL
0012FC84 00000003 |Mode = OPEN_EXISTING
0012FC88 00000080 |Attributes = NORMAL
0012FC8C 00000000 /hTemplateFile = NULL
0012FC90 0041F2B7 桌面鋼筆.0041F2B7

再運行又斷下

0012FC7C 004011A4 /CALL 到 SetFilePointer 來自 桌面鋼筆.004011A2 //移動指針
0012FC80 00000030 |hFile = 00000030
0012FC84 FFFFFFF8 |OffsetLo = FFFFFFF8 (-8.) //負數表示向前移動
0012FC88 00000000 |pOffsetHi = NULL
0012FC8C 00000002 /Origin = FILE_END //表示從文件的最後開始算

又F9一下

0012FC7C 00401220 /CALL 到 SetFilePointer 來自 桌面鋼筆.0040121E
0012FC80 00000030 |hFile = 00000030
0012FC84 FFF5F738 |OffsetLo = FFF5F738 (-657608.)
0012FC88 00000000 |pOffsetHi = NULL
0012FC8C 00000002 /Origin = FILE_END //又是從文件的最後算起

好了,如果你再按一次會發現還是用FILE_END這個參數

ok,知道什麼了沒有?

我來解釋一下用FILE_END參數表示指針從文件的最後開始移動,而附加數據無論在原來未脫殼的文件中和還是在脫殼的文件中,因爲我們複製的是整個overlay區域,他的overlay部分的數據對於最後的一個字節的距離是不變的。他每次都用FILE_END當然我們這樣複製就不需要重定位指針了,也就是說即使我們把原來的文件(從0地址開始)全部複製到脫殼後的文件中也是能夠運行的,因爲他是靠與最後的地址的距離來定位讀取數據。換個說法,如果這個時候你把最後一個數據刪掉,或者少複製哪怕是一個字節,就不能運行了。這也是我們爲什麼要先修復輸入表的原因。

總結:也就是說如果你把附加數據複製到脫殼後的程序發現能夠運行,呵呵~恭喜你,你碰巧遇到用FILE_END爲指針的程序。

4.下面我們來看看如果程序不用FILE_END的情況

還是以下面這個例子來說說吧

http://www.popbase.net/bbs/dispbbs.asp?BoardID=5&ID=1797

脫殼我就不說了。

打開區段發現這個附加數據的位置在4CA00+13724=60124(一個奇怪的位置^^)

複製以後發現不能運行,看來沒這麼好命,都是用FILE_END的啊!

我們要對比兩個程序,所以最好兩個都用OD載入,都到停在OEP處。

好了下斷bp SetFilePointer

這個是脫殼以後的程序的,運行以後斷下

0012FEBC 004091AA /CALL 到 SetFilePointer 來自 1_.004091A5
0012FEC0 00000168 |hFile = 00000168 (window)
0012FEC4 00060124 |OffsetLo = 60124 (393508.) //移動到60124處去
0012FEC8 00000000 |pOffsetHi = NULL
0012FECC 00000000 /Origin = FILE_BEGIN //從頭開始移動

再看看未脫殼的,發現是一模一樣的。這個60124不就是剛纔附加數據開始的地方嗎?

看來脫殼後的程序不管這麼多,還是訪問了原來的位置,導致不能運行。

好了,知道原因了以後我們就去解決他。關鍵是參數不對嘛~

ATL+F9返回看看他是怎麼壓棧的。

004091A0 /$ 51 push ecx ; /Origin
004091A1 |. 6A 00 push 0 ; |pOffsetHi = NULL
004091A3 |. 52 push edx ; |OffsetLo //這個EDX是關鍵
004091A4 |. 50 push eax ; |hFile
004091A5 |. E8 E2E0FFFF call ; /SetFilePointer
004091AA /. C3 retn //到這裏

F8 出來看看是什麼地方對EDX賦上60124的。
00413780 . 0FB7C9 movzx ecx,cx
00413783 . 8B40 04 mov eax,dword ptr ds:[eax+4]
00413786 . E8 155AFFFF call 1_.004091A0
0041378B . C3 retn //到這裏

還是沒什麼結果,F8 繼續

00493570 . A3 C87A4A00 mov dword ptr ds:[4A7AC8],eax
00493575 . 33C9 xor ecx,ecx
00493577 . 8B15 CC7A4A00 mov edx,dword ptr ds:[4A7ACC] //哈哈~~找到你了
0049357D . A1 C87A4A00 mov eax,dword ptr ds:[4A7AC8]
00493582 . 8B30 mov esi,dword ptr ds:[eax]
00493584 . FF56 0C call dword ptr ds:[esi+C] //從這裏call進去到SetFilePointer的
00493587 . E8 54FEFFFF call 1_.004933E0 //到這裏

原來是4A7ACC這的地址,對他下硬件寫入,看看是什麼時候寫入了這個60124

好了hw 4a7acc 重來,勝利在一步一步接近我們。

00493496 . A1 D07A4A00 mov eax,dword ptr ds:[4A7AD0] //靠~是4A7AD0傳給EAX的
0049349B . A3 CC7A4A00 mov dword ptr ds:[4A7ACC],eax //原來是這個EAX傳過去的
004934A0 . EB 07 jmp short 1_.004934A9 //斷在這裏,望上看

再來下HW 4A7AD0 重來。

0049927C |. C705 D07A4A00 24010>mov dword ptr ds:[4A7AD0],60124 //小樣,還找不到你
00499286 |. C605 B27A4A00 01 mov byte ptr ds:[4A7AB2],1 //斷在這裏

呵呵~~原來是0049927C這一句~~好了爆掉就可以了,把他改成你脫殼後的附加數據的開始地址就可以了。

總結一下:對於定位指針我們一般是依靠SetFilePointer這個函數來一步一步的尋找定位的問題的突破口,這裏各人有個人的方法我就不多說了,關鍵還是看你調試程序的功力如何。在看雪上,FLY大蝦的《VB函數速查.eXe 脫殼後附加數據的修復》http://bbs.pediy.com/showthread.php?threadid=8789
問題的實質並不是指針指錯了,而是雖然指對了overlay裏面的數據,可是讀出來的卻是關聯到未脫殼前的錯誤數據。可見,雖然是同一個問題但是實質和解決的辦法不可能千篇一律,就像解決校驗,解決overlay問題也需要具體問題具體分析。
--------------------------------------------------
3.總結
現在我想大家再回答開篇提出的幾個問題已經不難了,其實我在文中也有答案了,但是我要強調的一點是對於PE文件的區段映射的概念的理解深入將有助你理解overlay的問題。我在這也不多言了。

--------------------------------------------------
4.以後的思考
1.上面的那個60124是不是很奇怪啊,其實由於文件是對齊讀入內存的,這部分的數據也將被影射到內存空間,不行你可以dd 4cb724看看。這就是說如果我們把區段擴大,把overlay也讀入內存,雖然沒什麼用,但是卻嚴重影響了我們對overlay的開始的判斷!這種情況應該怎麼拌呢?
2.換個思路,如果我們把文件的讀寫用於檢驗PE文件,應該怎麼樣設計一個簡單的校驗呢?
--------------------------------------------------
5.後話
本來這篇文章打算到放假才放出來畢竟現在太忙了,而且有很多想法也還不成熟,還有待實踐,所以覺得這篇文章沒有深入下去不得不說是一個遺憾,不過趁着新年就算給大家一個交代吧。隨後我將進入緊張的複習階段。但是,在這裏還是要說一聲:祝論壇裏面的兄弟,新年快樂,萬事如意。
謝謝您能看完,如轉載請註明作者並保持文章的完整。

--------------------------------------------------------------------------------

 
發佈了48 篇原創文章 · 獲贊 8 · 訪問量 37萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章