CLI內核隨記(2)
今天有空,繼續調試上次的殼。該殼下載地址http://www.secureteam.net。上一次並沒有深入殼的解密流程,而是看了下虛方法的定位。今天的重點是殼解密流程。殼安裝後有一個GUI主程序,一個本地dll。調試的重點就是這個本地dll。
這次仍然下斷點在它掛鉤JIT的地方。怎樣激活這個斷點?只要運行一個還沒有JIT的方法既可。便於重復,這裡固定為顯示關於窗口。F9運行後,我們中斷在hook方法的入口處:
再看一下椎棧,椎頂的值0x79E9776F指向了mscorwks.dll中調用JIT的地方,而第二個0x790AF170則指向了JIT中調用compileMethod的地方,自然,一調用compileMethod就跳轉到hook的代碼中來了。
下面的一段代碼是調用CEEInfo::getMethodModule判斷是否是當前Module需要JIT。如果是,繼續執行解密過程,如果不是,自然不需要解密了。
012B2A4C |. 8B45 0C mov eax,dword ptr ss:[ebp+C]
012B2A4F |. 8B48 04 mov ecx,dword ptr ds:[eax+4]
012B2A52 |. 8B51 04 mov edx,dword ptr ds:[ecx+4]
012B2A55 |. 8B45 0C mov eax,dword ptr ss:[ebp+C]
012B2A58 |. 8B48 04 mov ecx,dword ptr ds:[eax+4]
012B2A5B |. 8B41 04 mov eax,dword ptr ds:[ecx+4]
012B2A5E |. 8B4D 0C mov ecx,dword ptr ss:[ebp+C]
012B2A61 |. 8D4401 04 lea eax,dword ptr ds:[ecx+eax+>
012B2A65 |. 8B4D 0C mov ecx,dword ptr ss:[ebp+C]
012B2A68 |. 8B5411 04 mov edx,dword ptr ds:[ecx+edx+>
012B2A6C |. 50 push eax
012B2A6D |. 8B42 30 mov eax,dword ptr ds:[edx+30]
012B2A70 |. FFD0 call eax;調用getMethodModule
012B2A72 |. 8945 F0 mov dword ptr ss:[ebp-10],eax
012B2A75 |. 8D4D F0 lea ecx,dword ptr ss:[ebp-10]
012B2A78 |. 51 push ecx
012B2A79 |. 8D55 F8 lea edx,dword ptr ss:[ebp-8]
012B2A7C |. 52 push edx
012B2A7D |. 8B8D 7CFFF>mov ecx,dword ptr ss:[ebp-84]
012B2A83 |. E8 88FEFFF>call CliSec_1.012B2910
012B2A88 |. 8D45 98 lea eax,dword ptr ss:[ebp-68]
012B2A8B |. 50 push eax
012B2A8C |. 8B8D 7CFFF>mov ecx,dword ptr ss:[ebp-84]
012B2A92 |. E8 89FAFFF>call CliSec_1.012B2520
012B2A97 |. 50 push eax
012B2A98 |. 8D4D F8 lea ecx,dword ptr ss:[ebp-8]
012B2A9B |. E8 80F7FFF>call CliSec_1.012B2220
012B2AA0 |. 0FB6C8 movzx ecx,al
012B2AA3 |. 85C9 test ecx,ecx
012B2AA5 |. 0F84 35020>je CliSec_1.012B2CE0;正常(需要解密)時這裡不會跳
緊接著代碼取方法的名稱:
012B2AAB |. 8D55 B4 lea edx,dword ptr ss:[ebp-4C]
012B2AAE |. 52 push edx
012B2AAF |. 8B45 10 mov eax,dword ptr ss:[ebp+10]
012B2AB2 |. 8B08 mov ecx,dword ptr ds:[eax]
012B2AB4 |. 51 push ecx
012B2AB5 |. 8B55 0C mov edx,dword ptr ss:[ebp+C]
012B2AB8 |. 8B42 04 mov eax,dword ptr ds:[edx+4]
012B2ABB |. 8B48 04 mov ecx,dword ptr ds:[eax+4]
012B2ABE |. 8B55 0C mov edx,dword ptr ss:[ebp+C]
012B2AC1 |. 8B42 04 mov eax,dword ptr ds:[edx+4]
012B2AC4 |. 8B50 04 mov edx,dword ptr ds:[eax+4]
012B2AC7 |. 8B45 0C mov eax,dword ptr ss:[ebp+C]
012B2ACA |. 8D5410 04 lea edx,dword ptr ds:[eax+edx+>
012B2ACE |. 8B45 0C mov eax,dword ptr ss:[ebp+C]
012B2AD1 |. 8B4C08 04 mov ecx,dword ptr ds:[eax+ecx+>
012B2AD5 |. 52 push edx
012B2AD6 |. 8B11 mov edx,dword ptr ds:[ecx]
012B2AD8 |. FFD2 call edx ;這裡調用.text:7A0A2503 CEEInfo::getMethodName
這兩大段代碼其實就是我在上一篇調試隨記中講的取vtable的過程,這裡略過。最後一句call edx後,看一下eax的值:EAX 11095453 ASCII "UxQ="。這說明了顯示關於窗口的方法名稱是”UxQ=”,顯示,這是混淆過的。下面的代碼調用getMethodDefFormMethod:
012B2ADA |. 8945 B0 mov dword ptr ss:[ebp-50],eax
012B2ADD |. 8B45 10 mov eax,dword ptr ss:[ebp+10]
012B2AE0 |. 8B08 mov ecx,dword ptr ds:[eax]
012B2AE2 |. 51 push ecx
012B2AE3 |. 8B55 0C mov edx,dword ptr ss:[ebp+C]
012B2AE6 |. 8B42 04 mov eax,dword ptr ds:[edx+4]
012B2AE9 |. 8B48 0C mov ecx,dword ptr ds:[eax+C]
012B2AEC |. 8B55 0C mov edx,dword ptr ss:[ebp+C]
012B2AEF |. 8B42 04 mov eax,dword ptr ds:[edx+4]
012B2AF2 |. 8B50 0C mov edx,dword ptr ds:[eax+C]
012B2AF5 |. 8B45 0C mov eax,dword ptr ss:[ebp+C]
012B2AF8 |. 8D5410 04 lea edx,dword ptr ds:[eax+edx+>
012B2AFC |. 8B45 0C mov eax,dword ptr ss:[ebp+C]
012B2AFF |. 8B4C08 04 mov ecx,dword ptr ds:[eax+ecx+>
012B2B03 |. 52 push edx
012B2B04 |. 8B51 38 mov edx,dword ptr ds:[ecx+38]
012B2B07 |. FFD2 call edx;調用.text:7A0A27FB CEEInfo::getMethodDefFromMethod
CEEInfo::getMethodDefFromMethod是沒有見過的函數,在遇到這種情況時,對於.net 2.0我們可以參考sscli的源代碼或直接看反匯編代碼。這裡直接看返回值,答案就明了了。EAX 060000B0,這個函數返回的就是方法的token。
緊接著的代碼比較是否是有效的token值,如果是,則繼續運行。代碼如下:
012B2B09 |. 8945 B8 mov dword ptr ss:[ebp-48],eax
012B2B0C |. 8B45 B8 mov eax,dword ptr ss:[ebp-48]
012B2B0F |. 25 FFFFFF0>and eax,0FFFFFF
012B2B14 |. 0F84 C6010>je CliSec_1.012B2CE0
緊接著代碼如下:
012B2B1A |. 8B4D B8 mov ecx,dword ptr ss:[ebp-48]
012B2B1D |. 81E1 FFFFF>and ecx,0FFFFFF
012B2B23 |. 894D A4 mov dword ptr ss:[ebp-5C],ecx
012B2B26 |. 8D4D F8 lea ecx,dword ptr ss:[ebp-8]
012B2B29 |. E8 72F9FFF>call CliSec_1.012B24A0
這裡012B2B26處的ecx=0013E890,指向了椎棧。調試到現在,我們並沒有跟蹤這個椎棧值的變化,更不可能知道它的作用。因此,重新調試(這就是選擇一個固定方法的好處,可重復性),並觀察該處值的改變。
首先記下此時的椎棧值:
0013E890 012D45AC CliSec_1.012D45AC
0013E894 012F2178
重新運行後再次中斷在hook方法的入口處,注意這時觀察椎棧的值,特別是0013E890處的變化。
入口處的值與剛才第一次調試過程中的值不一樣,因此直接在0013E890及0013E894兩處下硬件寫中斷。從中斷後返回地址可以知道,改變這兩處值的是我們忽略的一個函數:
012B2A83 |. E8 88FEFFF>call CliSec_1.012B2910
這個調用的具體過程,是下一篇的內容,呵呵。這裡,我們先放過它。接著向下走:
012B2B2E |. 8B50 04 mov edx,dword ptr ds:[eax+4]
012B2B31 |. 8955 AC mov dword ptr ss:[ebp-54],edx
012B2B34 |. 8B45 AC mov eax,dword ptr ss:[ebp-54]
012B2B37 |. 8B48 04 mov ecx,dword ptr ds:[eax+4]
012B2B3A |. 8B55 A4 mov edx,dword ptr ss:[ebp-5C]
012B2B3D |. 8B4491 FC mov eax,dword ptr ds:[ecx+edx*4>
012B2B41 |. 8945 A8 mov dword ptr ss:[ebp-58],eax
012B2B44 |. 8B4D F0 mov ecx,dword ptr ss:[ebp-10]
012B2B47 |. 51 push ecx ; /Arg1
012B2B48 |. 8B8D 7CFFF>mov ecx,dword ptr ss:[ebp-84] ; |
012B2B4E |. E8 8DF4FFF>call CliSec_1.012B1FE0 ; \CliSec_1.012B1FE0
如果跟進012B2B4E處的調用,你會發現這裡取得了主程序的基址0x11000000。再向下走,就是分配空間,填充解密過的方法體:
012B2B60 |. 8B55 A0 mov edx,dword ptr ss:[ebp-60]
012B2B63 |. 0355 A8 add edx,dword ptr ss:[ebp-58]
012B2B66 |. 8B85 7CFFF>mov eax,dword ptr ss:[ebp-84]
012B2B6C |. 8950 38 mov dword ptr ds:[eax+38],edx
012B2B6F |. 6A 1C push 1C
012B2B71 |. E8 F950000>call CliSec_1.012B7C6F ; new
012B2B76 |. 83C4 04 add esp,4
012B2B79 |. 8945 90 mov dword ptr ss:[ebp-70],eax
012B2B7C |. 837D 90 00 cmp dword ptr ss:[ebp-70],0
012B2B80 |. 74 1A je short CliSec_1.012B2B9C
012B2B82 |. 8B8D 7CFFF>mov ecx,dword ptr ss:[ebp-84]
012B2B88 |. 8B51 38 mov edx,dword ptr ds:[ecx+38]
012B2B8B |. 52 push edx
012B2B8C |. 8B4D 90 mov ecx,dword ptr ss:[ebp-70]
012B2B8F |. E8 2CF4FFF>call CliSec_1.012B1FC0
012B2B94 |. 8985 78FFF>mov dword ptr ss:[ebp-88],eax
因此,012B2B8F也是一處關鍵代碼,可以跟進去看是怎麼解密代碼的。(真正有沒有解密呢,跟進去就知道了 )不過在跟進去之前,先看一下椎棧。
注意棧頂的第一個值110A477C,細心的話會發現這個數值指向了哪裡:
Yes,它指向了.text節。而所有的IL方法都是存在.text節中的。不妨看一下該處的數據:
經常調試.net程序的可以一眼看出,圖中高亮的數據,不正是一個方法體嗎。接下來不用說了,改變參數後,調用真正的JIT的compileMethod:
012B2CD6 |. 8B55 10 mov edx,dword ptr ss:[ebp+10]
012B2CD9 |. C742 14 10>mov dword ptr ds:[edx+14],10
012B2CE0 |> 8B45 1C mov eax,dword ptr ss:[ebp+1C]
012B2CE3 |. 50 push eax ; /Arg6
012B2CE4 |. 8B4D 18 mov ecx,dword ptr ss:[ebp+18] ; |
012B2CE7 |. 51 push ecx ; |Arg5
012B2CE8 |. 8B55 14 mov edx,dword ptr ss:[ebp+14] ; |
012B2CEB |. 52 push edx ; |Arg4
012B2CEC |. 8B45 10 mov eax,dword ptr ss:[ebp+10] ; |
012B2CEF |. 50 push eax ; |Arg3
012B2CF0 |. 8B4D 0C mov ecx,dword ptr ss:[ebp+C] ; |
012B2CF3 |. 51 push ecx ; |Arg2
012B2CF4 |. 8B55 08 mov edx,dword ptr ss:[ebp+8] ; |
012B2CF7 |. 52 push edx ; |Arg1
012B2CF8 |. FF15 E4452>call dword ptr ds:[12D45E4] ; \mscorjit.7906E7F4
中斷在最後012B2CF8處,看一下傳給真正的JIT的參數:
要注意Arg3,因為它指向了修改後的IL體。跟進去看一下:
0013EA6C B8 65 A5 00 14 2C A5 00 88 47 0A 11 0E 00 00 00
IL方法體為110A4788,代碼大小0x0E,這正是剛才我們說的110A477C處的方法,指向啊原文件的.text節。這裡,又有一個想法:這段代碼倒底有沒有被解密呢?
我們用UltraEdit直接打開原文件,搜索字符串133001000E00000027000011731D0100060A066F0E01000A,可以找到兩處。根據110A477C對應的文件偏移,我們可以確定如下:
到這裡,可以下初步結論了:該殼並沒有加密原代碼,只是新建了方法體。在JIT時,將原代碼的正確偏移傳給JIT。
很懶的一個殼啊,如果要寫脫殼機,只需要直接在原文件中改偏移就行了。(沒有實踐,也許還需要更多工作。)小組誰有空來寫一個?