DLL文件脫殼
發(fā)表時間:2023-06-02 來源:明輝站整理相關軟件相關文章人氣:
[摘要]DLL文件的脫殼與EXE文件步驟差不多, 所不同的是, DLL文件多了個基址重定位表等要考慮。 在2003年出版的《加密與解密》(第二版)中以UPX, PECompact為例講述了DLL...
DLL文件的脫殼與EXE文件步驟差不多, 所不同的是, DLL文件多了個基址重定位表等要考慮。
在2003年出版的《加密與解密》(第二版)中以UPX, PECompact為例講述了DLL重定位重建的方法, 由于本人的思路限制, 當時只是從UPX, PECompact自身特點找思路解決這問題, 即先分析UPX, PECompact對重定位表處理算法, 然后寫工具逆算法還原重定位表, 如UPXAngela.exe等工具。 這種思路的通用性不好, 針對不同的殼和版本, 要重寫工具, 并且逆算法可能不完美, 從而存在bug。
后來, askformore在“重建重定位表腳本”一文中, 提出了一種更通用性的解決辦法, 利用外殼重定位相關數(shù)據(jù)時, 會根據(jù)外殼轉(zhuǎn)儲的重定位表確定要重定位的RVA, 完成代碼重定位工作。 將這些要重定位的RVA提取出來, 再將這些RVA根據(jù)重定位表的定義重新生成一份新的重定位表。 shoooo也曾提到過這個思路。 于是, 在第三版重寫這部分時, 根據(jù)這個思路寫了一款工具來完成這個重建功能, 詳見附件的ReloREC。 另外, ReloREC重構(gòu)重定位表的算法代碼, 參考了ccfer在看雪論壇.珠海金山2007逆向分析挑戰(zhàn)賽 第二階段第三題 提交的代碼。 在此一并表示感謝!
聲明:本文以第三版“13.5 DLL文件脫殼”一文和其他章節(jié)臨時整理組織, 稍有簡化, 可能有部分地方用詞和描述不是太連貫。
加殼的DLL處理重定位表有以下幾種情況:
1)完整的保留了原重定位表;
2)對原重定位表進行了加密處理;
等等
像ASPack,ASProtect等殼屬于第1種情況, 沒有加密重定位表, 脫殼后, 只需找到重定位的地址和大小即可。
像UPX, PECompact等殼屬于第2種情況, 必須重建重定位表, 這也是本文所要討論的, 本文以UPX為例來講述一下重定位的重建。
用UPX v3.01將EdrLib.dll文件加殼, 用PE工具查看其PE信息。
EntryPoint:E640h
ImageBase:400000h
13.5.1 尋找OEP
當DLL被初次映射到進程的地址空間中時, 系統(tǒng)將調(diào)用DllMain函數(shù), 當卸載DLL時也會再次調(diào)用DllMain函數(shù)。 也就是說, DLL文件相比EXE文件運行有一些特殊性, EXE的入口點只在開始時執(zhí)行一次, 而DLL的入口點在整個執(zhí)行過程中至少要執(zhí)行兩次。 一次是在開始時, 用來對DLL做一些初始化。 至少還有一次是在退出時, 用來清理DLL再退出。 所以DLL找OEP也有兩條路可以走, 一是載入時找, 另一方法是在退出時找。 而且一般來說前一種方法外殼代碼較復雜, 建議用第二種方法。
UPX殼比較簡單, 往下翻翻, 就可看到跳到OEP的代碼:
代碼:
003DE7F5 . 58 pop eax
003DE7F6 . 61 popad
003DE7F7 . 8D4424 80 lea eax, dword ptr [esp-80]
003DE7FB > 6A 00 push 0
003DE7FD . 39C4 cmp esp, eax
003DE7FF .^ 75 FA jnz short 003DE7FB
003DE801 . 83EC 80 sub esp, -80
003DE804 >- E9 372AFFFF jmp 003D1240 //跳到OEP
13.5.2 Dump映像文件
停在OEP后, 運行LordPE, 在進程窗口選擇loaddll.exe進程, 在下方窗口中的EdrLib.dll模塊上單擊右鍵, 執(zhí)行“dump full”菜單命令, 將文件抓取并保存到文件里, 如圖13.43所示。
dll.gif
圖13.45 抓取DLL內(nèi)存映像
對于DLL文件來說, Windows系統(tǒng)沒有辦法保證每一次運行時提供相同的基地址。 如果DLL基址所在內(nèi)存空間被占用或該區(qū)域不夠大, 系統(tǒng)會尋找另一個地址空間的區(qū)域來映射DLL, 此時外殼將對DLL執(zhí)行某些重定位操作。 從圖13.43得知, 此時DLL被映射到內(nèi)存的地址是03D000h, 與EdrLib.dll默認的基址400000h不同, 被重定位項所指向的地方是已經(jīng)重定位了的代碼數(shù)據(jù)。
例如這句:
代碼:
003D1266 A1 58B43D00 mov eax, dword ptr [3DB458]
為了得到與加殼前一樣的文件, 必須找到重定位的代碼, 跳過它, 讓其不被重定位。 重新加載DLL, 對上句重定位的地址3D1267h下內(nèi)存寫斷點, 中斷幾下, 就可來到重定位的處理代碼。
代碼:
003DE79E mov al, byte ptr [edi] ;指向UPX自己加密過的重定位表
003DE7A0 inc edi ;指針移向下一位
003DE7A1 or eax, eax ;EAX=0?結(jié)束標志
003DE7A3 je short 003DE7C7
003DE7A5 cmp al, 0EF
003DE7A7 ja short 003DE7BA
003DE7A9 add ebx, eax ;EBX的初值為(0xFFC+基址)
003DE7AB mov eax, dword ptr [ebx] ;EBX指向需要重定位的數(shù)據(jù), 取出放到EAX
003DE7AD xchg ah, al
003DE7AF rol eax, 10
003DE7B2 xchg ah, al
003DE7B4 add eax, esi ; ESI指向UPX0區(qū)塊的VA, 本例=3D1000
003DE7B6 mov dword ptr [ebx], eax ;重定位
003DE7B8 jmp short 003DE79C
003DE7BA and al, 0F
003DE7BC shl eax, 10
003DE7BF mov ax, word ptr [edi]
003DE7C2 add edi, 2
003DE7C5 jmp short 003DE7A9
003DE7C7 mov ebp, dword ptr [esi+E044] ;改好ESI為401000后, 按F4到這里
UPX殼己將原基址重定位表清零, 重定位操作時, 使用其自己的重定位表。 地址3DE7B4h處ESI指向UPX0區(qū)塊的VA, 本例為3D1000h, 為了讓代碼以默認ImageBase的值400000h重定位代碼, 可以在這句強制將ESI的值改為401000h。 來到這句后, 雙擊ESI寄存器, 改成401000h, 然后按F4來到3DE7C7h這時。 此時代碼段的數(shù)據(jù)沒被重定位, 可以Dump了。
代碼:
003D1253 833D 68AD4000 00 cmp dword ptr [40AD68], 0
運行LordPE將DLL映像抓取, 并保存為upx_dumped.dll。
13.5.3 重建DLL的輸入表
ImportREC能很好地支持DLL的輸入表的重建, 首先, 在Options里將“Use PE Header From Disk”默認的選項去除。 這是因為ImportREC需要獲得基址計算RVA值, DLL如果重定位了, 從磁盤取默認基址計算會導致結(jié)果錯誤。
1)在ImportREC下拉列表框中選擇DLL裝載器的進程, 此處為loaddll.exe進程。
2)單擊“Pick DLL”按鈕, 在DLL進程列表中選擇EdrLib.dll進程(見圖13.47)。
dll2.gif
圖13.47 選擇DLL進程
3)在OEP處, 填上DLL入口的RVA值1240h, 單擊IAT AutoSearch按鈕獲取IAT地址。 如果失敗, 必須手工判斷DLL的IAT位置和大小, 其RVA為7000h, Size為E8h。
4)單擊“Get Import”按鈕, 讓其分析IAT結(jié)構(gòu)重建輸入表。
5)勾選Add new section, 單擊“Fix Dump”按鈕, 并選擇剛抓取的映像文件dumped.dll, 它將創(chuàng)建一個dumped_.dll文件。
13.5.4 構(gòu)造重定位表
原理請參考本文開始處的說明。
先來回顧一個重定位表的結(jié)構(gòu):
代碼:
IMAGE_BASE_RELOCATION STRUCT
VirtualAddress dd 0
SizeOfBlock dd 0
Type1 dw 0; 其中:Bit15—Bit12為類型 type, Bit11--Bit0 為ItemOffset
IMAGE_RELOCATION ENDS
重定位表以1000h大小為一個段, 因為ItemOffset最長為12位, 即剛好為1000h。 如果還有更多段, 將重復上面數(shù)據(jù)結(jié)構(gòu), 直到VirtualAddress為NULL, 表示結(jié)束。
ReloREC工具可以根據(jù)一組重定位的RVA, 重新構(gòu)造一個新的重定位表。 首先要做的工作是將UPX外殼這些要重定位的RVA提取出來。
在處理重定位代碼語句中, 下面這句就是對代碼重定位, 其中EBX保存的就是要重定位的地址。
代碼:
003DE7B6 mov dword ptr [ebx], eax ;EBX指向要重定位的RVA
補丁的思路是找塊代碼空間, 跳過去執(zhí)行補丁代碼, 將重定位的地址轉(zhuǎn)成RVA, 并保存下來。 如下語句跳到補丁代碼處:
代碼:
003DE7B8 jmp short 003DE80A
我們鍵入的補丁代碼:
003DE80A pushad
003DE80B mov edx, dword ptr [3E0000] ;從全局變量3E0000h取一地址指針
003DE811 sub ebx, 3D0000 ;減外殼基址, 將ebx中的地址轉(zhuǎn)成RVA
003DE817 mov dword ptr [edx], ebx ;將獲得的RVA保存下來
003DE819 add edx, 4 ;指向下一個DWRD地址
003DE81C mov dword ptr [3E0000], edx ;將指針保存到全局變量中
003DE822 popad
003DE823 jmp 003DE79C ;跳回外殼代碼
3E0000h這個地址是OllyDbg的插件HideOD臨時分配的, 其初始值設為3E0010h, 如圖13.71。
dll3.gif
補丁代碼鍵入完成后, 外殼在處理重定位相關代碼時, 這段補丁代碼將需要重定位的RVA全部提取出來。 執(zhí)行完補丁代碼, 數(shù)據(jù)窗口將保存需要重定位的RVA,
將需要重定位的RVA復制出來(選取數(shù)據(jù)時, 最后一個DWORD數(shù)據(jù)是0), 操作時單擊鼠標右鍵, 執(zhí)行菜單Binary/Binary copy(二進制復制)功能, 再運行WinHex, 新建一文檔, 將這段二進制數(shù)據(jù)粘貼進去, 粘貼時, 選擇ASCII Hex模式(圖13.52), 然后將提取的數(shù)據(jù)保存為Relo.bin。
Relo.bin中保存的就是需要重定位的地址, 以RVA表示。 部分數(shù)據(jù)如下:
代碼:
0000101D
00001031
0000106E
0000108D
000010A1
000010DE
000010FB
00001109
0000110F
……
ReloREC這款工具, 就是根據(jù)這些RVA重新生成一份新的重定位表
準備工作完成, 運行ReloREC, 將Relo.bin拖放到ReloREC主界面上可打開此文件, 如圖13.53。 然后在dumped_.dll里找一塊空白代碼處保存重定位表(一般在UPX1或UPX2區(qū)塊里找), 在這選擇C000h處。 在Relocation's RVA域里填上原始重定位表的RVA地址, 本例為C000h, 最后單擊“Fix Dump”按鈕, 打開上節(jié)剛修復輸入表的dumped_.dll文件, 即可完成重定位表的修復。
上面是電腦上網(wǎng)安全的一些基礎常識,學習了安全知識,幾乎可以讓你免費電腦中毒的煩擾。