史萊姆論壇

返回   史萊姆論壇 > 教學文件資料庫 > 程式 & 網頁設計技術文件
忘記密碼?
論壇說明 標記討論區已讀

歡迎您來到『史萊姆論壇』 ^___^

您目前正以訪客的身份瀏覽本論壇,訪客所擁有的權限將受到限制,您可以瀏覽本論壇大部份的版區與文章,但您將無法參與任何討論或是使用私人訊息與其他會員交流。若您希望擁有完整的使用權限,請註冊成為我們的一份子,註冊的程序十分簡單、快速,而且最重要的是--註冊是完全免費的!

請點擊這裡:『註冊成為我們的一份子!』

Google 提供的廣告


 
 
主題工具 顯示模式
舊 2006-02-13, 12:35 PM   #1 (permalink)
榮譽會員
 
psac 的頭像
榮譽勳章
UID - 3662
在線等級: 級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時
註冊日期: 2002-12-07
住址: 木柵市立動物園
文章: 17381
現金: 5253 金幣
資產: 33853 金幣
預設 nspack3.5主程序脫殼分析(Aspr SKE 2.X)

nspack3.5主程序脫殼分析(Aspr SKE 2.X)

nspack 3.5 主程序脫殼介紹
xp sp2
flyodbg
Aspr SKE 2.X


零 需要哪裡就重新來過重點分析哪裡
come on let's go


一 PEiD可以不用, 但LordPE一定要先載入看看
.rsrc段上面有三個區段,沒有名字,不過可以猜到是.text、.rdata和.data段,是VC的程序


二 看看能不能在OD下跑起來
OD載入nspack.exe,忽略所有異常,清除所有斷點, 打上IsDebuggerPresent插件
F9執行 gogogo~
正常情況下能跑起來,alt+e看看載入的dll,看到有msvcrt.dll,沒有發現mfc的dll
所以是普通VC或MFC靜態
我猜我猜我猜猜猜


三 到oep看看
重來,OD截入,忽略所有...清除...打上..插件
到GetVersion的末尾retn下斷

7C8114AB kernel32.GetVersion 64:A1 18000000 mov eax,dword ptr fs:[18]
7C8114B1 8B48 30 mov ecx,dword ptr ds:[eax+30]
7C8114B4 8B81 B0000000 mov eax,dword ptr ds:[ecx+B0]
7C8114BA 0FB791 AC000000 movzx edx,word ptr ds:[ecx+AC]
7C8114C1 83F0 FE xor eax,FFFFFFFE
7C8114C4 C1E0 0E shl eax,0E
7C8114C7 0BC2 or eax,edx
7C8114C9 C1E0 08 shl eax,8
7C8114CC 0B81 A8000000 or eax,dword ptr ds:[ecx+A8]
7C8114D2 C1E0 08 shl eax,8
7C8114D5 0B81 A4000000 or eax,dword ptr ds:[ecx+A4]
7C8114DB C3 retn //這裡下斷

F9執行,斷下,F8返回,向上看看,看到oep了

00486C68 55 push ebp
00486C69 8BEC mov ebp,esp
00486C6B 6A FF push -1
00486C6D 68 38FB4A00 push nSpack.004AFB38
00486C72 68 50554800 push nSpack.00485550
00486C77 64:A1 00000000 mov eax,dword ptr fs:[0]
00486C7D 50 push eax
00486C7E 64:8925 00000000 mov dword ptr fs:[0],esp
00486C85 83EC 58 sub esp,58
00486C88 53 push ebx
00486C89 56 push esi
00486C8A 57 push edi
00486C8B 8965 E8 mov dword ptr ss:[ebp-18],esp
00486C8E FF15 6C724A00 call dword ptr ds:[4A726C] ; kernel32.GetVersion
00486C94 33D2 xor edx,edx // GetVersion返回到這裡

VC6會GetVersion,VC7會GetVersionExA,可以都在末尾下斷,到時候看哪個像oep附近就是了


四 輸入表
GetVersion是在[4A726C],那麼到那個地方向上看看,向下看看,找輸入表的範圍
結果
4A7000 到 4A7688
輸入表沒有加密
有時候aspr SKE 2.X會把輸入表加密,把一部分匯入函數位址改的亂七八糟,可那些加密的位址是不存在的。
那它怎麼用那裡的匯入函數呢? 其實它把程式碼中所有對加密匯入函數的使用從原先的call [IAT]或jmp [IAT]
改成了call 00EA0000這種樣子,從它自己的call 00EA0000進入匯入函數,這樣那些加密的匯入函數就可以隨便
寫一個不存在的位址了。

如果輸入表加密了,你可以這樣完整修復:
OD截入,忽略所有...清除...打上..插件
隨便對一個匯入函數位址下記憶體寫斷點,比如這裡GetVersion的4A726C
斷了若干次後到這裡
00C5764D 8902 mov dword ptr ds:[edx],eax ; // eax指向GetVersion的位址,寫入ebx = 4A726C
00C5764F E9 20010000 jmp 00C57774

00C57774 8B45 0C mov eax,dword ptr ss:[ebp+C]
00C57777 8300 04 add dword ptr ds:[eax],4
00C5777A 8D85 FAFEFFFF lea eax,dword ptr ss:[ebp-106]
00C57780 3BF8 cmp edi,eax
00C57782 74 07 je short 00C5778B
00C57784 8BC7 mov eax,edi
00C57786 E8 D9ADFDFF call 00C32564
00C5778B 5F pop edi
00C5778C 5E pop esi
00C5778D 5B pop ebx
00C5778E 8BE5 mov esp,ebp
00C57790 5D pop ebp
00C57791 C2 1000 retn 10 // F8下來後這裡返回

返回後
00C5795A E8 59FCFFFF call 00C575B8 //關鍵的call 進去看
00C5795F 0FB707 movzx eax,word ptr ds:[edi] //上面返回後是回到這裡
00C57962 83C0 02 add eax,2
00C57965 03F8 add edi,eax
00C57967 8A1F mov bl,byte ptr ds:[edi]
00C57969 47 inc edi
00C5796A 3A5E 34 cmp bl,byte ptr ds:[esi+34]
00C5796D ^ 0F85 77FFFFFF jnz 00C578EA //繼續當前dll的下一個匯入函數
00C57973 8BDF mov ebx,edi
00C57975 8B03 mov eax,dword ptr ds:[ebx]
00C57977 85C0 test eax,eax
00C57979 ^ 0F85 0AFFFFFF jnz 00C57889 //下一個dll

C575B8這個call就是對輸入表的處理

00C575B8 55 push ebp
00C575B9 8BEC mov ebp,esp
00C575BB 81C4 F8FEFFFF add esp,-108
00C575C1 53 push ebx
00C575C2 56 push esi
00C575C3 57 push edi
00C575C4 8B55 14 mov edx,dword ptr ss:[ebp+14]
00C575C7 8B5D 08 mov ebx,dword ptr ss:[ebp+8]
00C575CA 8DBD FAFEFFFF lea edi,dword ptr ss:[ebp-106]
00C575D0 8BC2 mov eax,edx
00C575D2 48 dec eax
00C575D3 83E8 02 sub eax,2
00C575D6 0FB630 movzx esi,byte ptr ds:[eax]
00C575D9 8B45 10 mov eax,dword ptr ss:[ebp+10]
00C575DC 83E8 02 sub eax,2
00C575DF 0FB600 movzx eax,byte ptr ds:[eax]
00C575E2 3B43 2C cmp eax,dword ptr ds:[ebx+2C]
00C575E5 76 06 jbe short 00C575ED //上面不去管它,這個跳轉肯定滿足

00C575ED 33C0 xor eax,eax
00C575EF 8A43 3B mov al,byte ptr ds:[ebx+3B]
00C575F2 3BF0 cmp esi,eax // 這裡開始了4種情況的比較
00C575F4 75 5E jnz short 00C57654

C575F2的 cmp esi, eax開始了4種檔案類型的比較
當前匯入函數的檔案類型是放在esi中,你可以在這裡下個斷點,然後一個一個看下來
第1種檔案類型:用第1個密鑰,還原真實匯入函數位址,這裡不防設esi值為1
第2種檔案類型:用第2個密鑰,還原真實匯入函數位址,這裡不防設esi值為2
第3種檔案類型:用第2個密鑰,不作任何處理,這裡不防設esi值為3
第4種檔案類型:GetProcAddress,這裡不防設esi值為4

可見那些加密的匯入函數位址,也就是第3種檔案類型,與其說是加密,不如說是殼沒有去處理
既然它和第2種檔案類型處理方式一樣,可以在cmp esi, eax這個點,修改esi中的值,把第3種
情況改成第2種情況就可以了
或者你也可以跑下去,把一些jnz或je改成magic jmp,讓第3種情況跑到第2種情況也可以

需要說明的是esi的對每個aspr加殼的程序都是隨機的,只要多看幾個,就知道是哪個改哪個了



五 取得call 00EA0000的所有位址
按照上面所說的,可以在GetVerion返回後dump出來,然後用ImortREC修復輸入表,把oep 86c68寫回去
不妨叫做unpack1.exe,用od載它跑一下,它會告訴你call 00EA0000掛了,然後按F12(pause),從堆疊的
返回位址知道是這個讓你掛了
00489AAB E8 5065A100 call 00EA0000
00489AB0 1283 4E04FF6A adc al,byte ptr ds:[ebx+6AFF044E]

EA0000是什麼呢?
它是把匯入函數使用的變形,原來的call [IAT] 和 jmp [IAT]的變形
EA0000是殼用VirtualAlloc的空間,不在區段中
在我的機電腦上現在是call 00EA0000,在你的電腦上就可能是call 1230000
也就是說,call 00EA0000是殼經過計算後寫入的
於是我想看看,在它寫入call 00EA0000前是什麼樣子


OD載入nspack.exe,忽略所有異常,清除所有斷點, 打上IsDebuggerPresent插件
對489AAC下記憶體寫入斷點 (因為489AAB是'E8',我們要的是後4個字元)

若干次後會斷在這裡
00C5BAD3 8945 00 mov dword ptr ss:[ebp],eax // 斷在這兒:ebp指向489AAC,eax寫入後,使那個地方變成call 00EA0000
00C5BAD6 6A 0A push 0A
00C5BAD8 E8 7F9AFEFF call 00C4555C
00C5BADD 8BC8 mov ecx,eax
00C5BADF 038B E4000000 add ecx,dword ptr ds:[ebx+E4]
00C5BAE5 8BD6 mov edx,esi
00C5BAE7 8BC3 mov eax,ebx
00C5BAE9 E8 8EE5FFFF call 00C5A07C
00C5BAEE FF0C24 dec dword ptr ss:[esp]
00C5BAF1 03B3 E4000000 add esi,dword ptr ds:[ebx+E4]
00C5BAF7 833C24 00 cmp dword ptr ss:[esp],0
00C5BAFB ^ 0F87 55FEFFFF ja 00C5B956 //如果還有需要處理就跳上去
00C5BB01 53 push ebx
00C5BB02 E8 5D000000 call 00C5BB64
00C5BB07 0183 EC000000 add dword ptr ds:[ebx+EC],eax
00C5BB0D B0 01 mov al,1
00C5BB0F 83C4 24 add esp,24
00C5BB12 5D pop ebp
00C5BB13 5F pop edi
00C5BB14 5E pop esi
00C5BB15 5B pop ebx
00C5BB16 C3 retn //這裡結束


正如我所說,call 00EA0000完全是在程式碼段解碼後,申請空間,現在我申請到的是EA0000
那麼它就將需要變形的地方計算後寫成call 00EA0000,如果你申請到的是1230000,那麼你
是call 1230000

斷在這裡,我當然想看一看在改寫成call 00EA0000之前,那些位址是不是正常的
很可惜,那裡在改寫成call 00EA0000,本身就是亂掉的。
或者在某個時候能知道那些變形位址原先的真實情況,可惜我找不到。
也許只有作者知道在哪裡
也許根本就找不到
因為根本就不需要
對於call 00EA0000,它加密前只要知道2件事,1.本身所在的位址 2.IAT中的位置
對於call 00EA0000,現在也只要知道2件事,1.本身所在的位址 2.最後要去的匯入函數的位址
它沒有理由記錄IAT中的位置
我們要做的是找出最後到達的匯入函數的位址,然後找出它在IAT中的位置
改成原先的call [IAT] 或 jmp [IAT]

回到正題,當我們斷下時,前面可能已經處理若干個了
要想得到全部的表
你有好幾種選項
1. 到oep後,寫一段程式碼搜尋出所有的call 00EA0000的位址
2. 想辦法第一時間斷在上面這個地方,即00C5BAD3,ebp-1就是變形的位址,儲存所有的ebp-1
3. 也許記憶體中本身存在這張表,我沒有去找,你可以找找

要找全他們並不難

啊,還有一個要說明的
在寫入每一處的call 00EA0000時,上面的流程會經過這裡
00C5B981 FFD2 call edx //call edx 結果在eax
00C5B983 807B 20 00 cmp byte ptr ds:[ebx+20],0 // eax 可能是1或0
00C5B987 0F85 3D010000 jnz 00C5BACA

如果是1,當前這個call 00EA0000處執行時,會重新回到使用位址,再進去入匯入函數
如果是0,當前這個call 00EA0000進入匯入函數後出來(好像是廢話),不過這種方式比較邪惡,它可能做更多的事情
下面我會講到



六 call 00EA0000的修復
有沒有想過一個有意思的問題,所有這樣的使用都是進入EA0000一個地方,但是殼卻知道最後
目的位址是哪一個匯入函數,它是怎麼判斷的呢?
當到了EA0000,殼能看到什麼?
1. 參數
2. 返回位址
第1種情況:鬼知道我會傳什麼參數,多少個參數,它不能作為評判標準的
第2種情況:只有你了,Aspr存著一張表,它記錄了所有call 00EA0000的返回位址和最後匯入函數的1對1關係

它是加密的
我們要做的是找出這張表,或者找到1個點能確定它們1對1的關係

簡單說一下進入EA0000後發生了什麼,一共三層

第一層:儲存所有當前暫存器 (出來後還要繼續執行的,不能影響後面,不過它不是明目張膽的pushad)
第二層:1. 決定是哪一種方式的匯入函數使用
a. 第一種方式:將call 00EA0000 變成call F00004之類,出來後再次從原地進入F00004進入匯入函數
b. 第二種方式:直接帶著參數進入匯入函數
2. 決定這個使用是call (ff15)還是jmp (ff25)
不要以為C的都是call,delphi的都是jmp
c. 如果是call (ff15),返回位址要+1 ,比如inc [esp],因為call 00EA0000 占灌水限制節,call (ff15)占6個字元
d. 如果是jmp (ff25),要esp+4,想一下就知道原因了
3. 如果是1.b的情況,可能有更邪惡的對下一行的偷程式碼,我一直沒有找到好的方式解決它
第三層:恢復所有的暫存器返回


對於第一層的和第三層的操作,只要一路F7即可
當你看到
00EA0166 2BDA sub ebx,edx
00EA0168 FFD3 call ebx //F7進入第二層
就知道要F7進入第二層了,當然別的aspr的殼可能是call eax或call esi等等
到了第二層,程式碼比較工整了,可以一路F8
最後
00EB00B9 5C pop esp
00EB00BA FF6424 FC jmp dword ptr ss:[esp-4] //從第三層返回
是第三層回來,上面已提到,回來可能是回到原處call到一個新的地方進入匯入函數,也可能就是完成回來


因此重點講講第二層
一路F8
可以看到這裡
00C5B48F /75 63 jnz short 00C5B4F4 //比較call 00EA0000 返回位址的密文,不是就跳上去繼續找
00C5B491 |807B 20 00 cmp byte ptr ds:[ebx+20],0 //找到了當前call 00EA0000的處理情況了
00C5B495 |74 3C je short 00C5B4D3
00C5B497 |8B45 E4 mov eax,dword ptr ss:[ebp-1C]
00C5B49A |0FB640 09 movzx eax,byte ptr ds:[eax+9]
00C5B49E |8D0440 lea eax,dword ptr ds:[eax+eax*2]
00C5B4A1 |8B5483 68 mov edx,dword ptr ds:[ebx+eax*4+68]
00C5B4A5 |8B45 FC mov eax,dword ptr ss:[ebp-4]
00C5B4A8 |FFD2 call edx //和第五章最後說的情況一下,再次比較是哪一種方式
00C5B4AA |3C 01 cmp al,1 //eax為1則是a情況,為0則是b情況
00C5B4AC |75 25 jnz short 00C5B4D3
00C5B4AE |56 push esi
00C5B4AF |8D45 FC lea eax,dword ptr ss:[ebp-4]
00C5B4B2 |50 push eax
00C5B4B3 |8B45 14 mov eax,dword ptr ss:[ebp+14]
00C5B4B6 |50 push eax
00C5B4B7 |8B45 18 mov eax,dword ptr ss:[ebp+18]
00C5B4BA |50 push eax
00C5B4BB |8B45 0C mov eax,dword ptr ss:[ebp+C]
00C5B4BE |50 push eax
00C5B4BF |8B45 F0 mov eax,dword ptr ss:[ebp-10]
00C5B4C2 |50 push eax
00C5B4C3 |8B4D 1C mov ecx,dword ptr ss:[ebp+1C]
00C5B4C6 |8B55 10 mov edx,dword ptr ss:[ebp+10]
00C5B4C9 |8BC3 mov eax,ebx
00C5B4CB |E8 C0010000 call 00C5B690 // a情況這裡F7進去
00C5B4D0 |EB 01 jmp short 00C5B4D3
00C5B4D2 |E8 8D45FC50 call 51C1FA64
00C5B4D7 |8B45 14 mov eax,dword ptr ss:[ebp+14]
00C5B4DA |50 push eax
00C5B4DB |8B45 18 mov eax,dword ptr ss:[ebp+18]
00C5B4DE |50 push eax
00C5B4DF |8B45 0C mov eax,dword ptr ss:[ebp+C]
00C5B4E2 |50 push eax
00C5B4E3 |8B45 F0 mov eax,dword ptr ss:[ebp-10]
00C5B4E6 |50 push eax
00C5B4E7 |8B4D 1C mov ecx,dword ptr ss:[ebp+1C]
00C5B4EA |8B55 10 mov edx,dword ptr ss:[ebp+10]
00C5B4ED |8BC3 mov eax,ebx
00C5B4EF |E8 64F1FFFF call 00C5A658 // b情況這裡F7進去


先看a情況吧,進去後一路F8

很快到了這裡
00C5B7DD 8B45 F4 mov eax,dword ptr ss:[ebp-C]
00C5B7E0 8B80 E0000000 mov eax,dword ptr ds:[eax+E0]
00C5B7E6 0345 E4 add eax,dword ptr ss:[ebp-1C]
00C5B7E9 8945 FC mov dword ptr ss:[ebp-4],eax //到了這裡eax的值就是導函數的位址了
不過我覺得這個點不太好,再往下F8
00C5B7EC 33C0 xor eax,eax
00C5B7EE 8AC3 mov al,bl
00C5B7F0 0145 10 add dword ptr ss:[ebp+10],eax
00C5B7F3 57 push edi
00C5B7F4 6A 00 push 0
00C5B7F6 8D4D E0 lea ecx,dword ptr ss:[ebp-20]
00C5B7F9 8B45 F4 mov eax,dword ptr ss:[ebp-C]
00C5B7FC 8B40 3C mov eax,dword ptr ds:[eax+3C]
00C5B7FF 8B55 FC mov edx,dword ptr ss:[ebp-4]
00C5B802 E8 6DB9FFFF call 00C57174
00C5B807 8945 FC mov dword ptr ss:[ebp-4],eax
00C5B80A 8B45 E0 mov eax,dword ptr ss:[ebp-20]
00C5B80D 8B00 mov eax,dword ptr ds:[eax]
00C5B80F E8 C0E6FFFF call 00C59ED4
00C5B814 8BD0 mov edx,eax
00C5B816 0255 DF add dl,byte ptr ss:[ebp-21]
00C5B819 8B4D FC mov ecx,dword ptr ss:[ebp-4] //這個點比較好

到了這裡 [ebp-4C]是我們需要的匯入函數的位址,dl中的值決定了是call(ff15)還是jmp(ff25)
dl中的值不同的程序是隨機,找幾個call 00EA0000進去出來看一下就知道現用的程序中哪個對應ff15,哪個對應ff25了


再來看看b情況,進去後也是一路F8

00C5A7E7 3A45 EF cmp al,byte ptr ss:[ebp-11] //al和a情況中的dl一樣,決定是ff15還是ff25
00C5A7EA 0F85 9C000000 jnz 00C5A88C
00C5A7F0 EB 01 jmp short 00C5A7F3

ff15和ff25產生的分支分別能到下面
00C5A7F3 8B45 F4 mov eax,dword ptr ss:[ebp-C]
00C5A7F6 8B80 E0000000 mov eax,dword ptr ds:[eax+E0]
00C5A7FC 0145 FC add dword ptr ss:[ebp-4],eax

00C5A8A5 8B45 F4 mov eax,dword ptr ss:[ebp-C]
00C5A8A8 8B80 E0000000 mov eax,dword ptr ds:[eax+E0]
00C5A8AE 0145 FC add dword ptr ss:[ebp-4],eax
C5A7FC或C5A8AE做完後[ebp-4] 是我們需要的匯入函數的位址
再看看[ebp-2c],如果它是FFFFFFFF,說明這個匯入函數使用是乾淨的
如果它有值,表示它的下一行也偷了。具體處理可能對它下個硬體訪問斷點再跟蹤
不過我比較沒耐心
我喜歡把不乾淨的這些地方扣出來
然後跑過去猜
一般偷的都是
mov esi,eax 或 mov edi,eax等等
找到了這些點,寫指令碼也好,寫程式碼恢復也好,修復就不難了



七 stolen oep
這個例子中沒有stolen oep,所以沒什麼好講,有興趣的看看loveboom的文章
文章可能比較老,但是現在還是適用的



八 最後一些說明
到了這裡差不多結束了,你可以像syscom那樣,掃瞄所有有匯入函數變形位址進行修復了
其實把原理搞清楚了,修復的時候就算碰到狀況也就能知道怎麼處理
脫aspr並不需要從頭跟到尾,只要重點的地方耐心分析就可以了,只要耐心,你能發現更多一些東西
__________________
http://bbsimg.qianlong.com/upload/01/08/29/68/1082968_1136014649812.gif
psac 目前離線  
送花文章: 3, 收花文章: 1631 篇, 收花: 3205 次
 


主題工具
顯示模式

發表規則
不可以發文
不可以回覆主題
不可以上傳附加檔案
不可以編輯您的文章

論壇啟用 BB 語法
論壇啟用 表情符號
論壇啟用 [IMG] 語法
論壇禁用 HTML 語法
Trackbacks are 禁用
Pingbacks are 禁用
Refbacks are 禁用


所有時間均為台北時間。現在的時間是 12:22 PM


Powered by vBulletin® 版本 3.6.8
版權所有 ©2000 - 2024, Jelsoft Enterprises Ltd.


SEO by vBSEO 3.6.1