?? 對一個用win32asm編寫的tcp端口掃描程序的分析.txt
字號:
標 題: 【原創(chuàng)】對一個用Win32Asm編寫的TCP端口掃描程序的分析
作 者: jhkdiy
時 間: 2006-03-25,00:35
鏈 接: http://bbs.pediy.com/showthread.php?t=23110
作者:jhkdiy
郵件:jhkdiy_gzb@21cn.net
論壇:www.20cn.net
日期:06年3月24日
有一段時間沒有去過ASMCommunity Messageboard(http://www.asmcommunity.net/board/index.php)了,以前直接可以訪問。
現(xiàn)在只能通過代理才能訪問,至少我這邊是這樣。在里面隨便逛了逛,無意看到一位論壇會員問怎樣用Asm來做一個端口掃描器,
結(jié)果其他會員給了一個源代碼,在window的控制臺窗口下運行,以阻塞模式掃描一個目標主機的端口。我覺得這個代碼對剛學習
Windows Socket編程的朋友來說很有啟發(fā)性,所以現(xiàn)在詳細講解一下這份代碼,希望對大家有所幫助。若各位讀者想先試試該程序
可以先用RadAsm建立一個console工程,然后將下面的代碼粘貼到asm文件中,編譯&鏈接。在控制臺下運行該程序即可。
源代碼如下,只做了一點的格式編排:
代碼:
; Conscan - CLI "test"
.386
.model flat, stdcall
option casemap :none
include windows.inc
include user32.inc
include kernel32.inc
include masm32.inc
include wsock32.inc
includelib user32.lib
includelib kernel32.lib
includelib masm32.lib
includelib wsock32.lib
print macro lpszText:VARARG
local txt
.data
txt db lpszText,13,10,0
.code
invoke StdOut,addr txt
ENDM
SOCKADDR_IN struct
sin_family WORD ?
sin_port WORD ?
sin_addr DWORD ?
sin_zero BYTE 8 dup (?)
SOCKADDR_IN ends
.data
szusg db "usage: conscan <hostname>",13,10,0
fmt db "%d OPEN",13,10,0
sa SOCKADDR_IN <>
wsa WSADATA <>
sfd dd 0
port dd 0
pl dd 21
dd 22
dd 23
dd 25
dd 80
dd 137
dd 350
dd 8080
dd 6667
dd 31337
dd 0
.data?
hostname db 2024 dup (?)
buffer db 100 dup (?)
.code
conscan:
call main
call ExitProcess
main proc
invoke GetCL,1,addr hostname
cmp eax,1
jne Arg_Error
mov sa.sin_family, AF_INET
lea edi,pl
Port_Scan_Loop:
mov eax,[edi]
cmp eax,0
je Port_Scan_Complete
inc edi
mov port,eax
invoke WSAStartup,101h,addr wsa
invoke socket, AF_INET, SOCK_STREAM, 0
mov sfd,eax
invoke htons, port
mov sa.sin_port, ax
invoke gethostbyname, addr hostname
mov eax,[eax+12]
mov eax,[eax]
mov eax,[eax]
mov sa.sin_addr,eax
invoke connect,sfd,addr sa,SIZEOF sa
cmp eax, 0
jne Port_Closed
invoke wsprintf,addr buffer,addr fmt,port
invoke StdOut,addr buffer
Port_Closed:
invoke closesocket,sfd
call WSACleanup
jmp Port_Scan_Loop
Arg_Error:
invoke StdOut,addr szusg
ret
Port_Scan_Complete:
print "-- Scan Complete --"
ret
main endp
end conscan
整份代碼才100行左右,那么,這短短的100行代碼到底做了些什么呢?首先請將代碼看一遍,
如果是似明非明的話就需要繼續(xù)看下面的解釋了。我們就一段段來分析吧,首先是第一部分:
.386
.model flat, stdcall
option casemap :none
include windows.inc
include user32.inc
include kernel32.inc
include masm32.inc
include wsock32.inc
includelib user32.lib
includelib kernel32.lib
includelib masm32.lib
includelib wsock32.lib
開頭的部分相信任何一位學win32asm的朋友都能看懂,include 和 includelib的部分確實有需要
留意的部分,那就是:
include wsock32.inc
includelib wsock32.lib
編寫windows socket程序就要包含相應的windows socket頭文件和庫文件,當前可以使用的winsock
接口動態(tài)鏈接庫版本有1.1版本和2.0版本。wsock32.inc & wsock32.lib 文件表示使用32位的
1.1版本的winsock,如果是使用2.0的winsock就需要使用WS2_32.inc 和 WS2_32.lib 文件。如果
對masm32.inc也不熟悉的話,在這里簡單的說說,它是一系列常用的宏和函數(shù)的集合,主要是為了
編程方便,減少重復編寫同樣的代碼而做的,包含了許多實用的宏和函數(shù),在這個代碼中StdOut等函數(shù)
就使用了這個文件。
再往下看,看到了一個宏:
print macro lpszText:VARARG
local txt
.data
txt db lpszText,13,10,0
.code
invoke StdOut,addr txt
ENDM
該宏即使用了masm32.inc中的StdOut函數(shù),該函數(shù)在windows標準控制臺下輸出一段文本,有點像C語言下的
printf()函數(shù)。緊跟著看到一段結(jié)構(gòu)體定義:
SOCKADDR_IN struct
sin_family WORD ?
sin_port WORD ?
sin_addr DWORD ?
sin_zero BYTE 8 dup (?)
SOCKADDR_IN ends
該結(jié)構(gòu)用來將套接字綁定到IP地址和端口使用,當然在后續(xù)的接受和發(fā)送等等的工作中都會用到。各字段注釋
如下:
SOCKADDR_IN struct
sin_family WORD ? ;地址格式
sin_port WORD ? ;端口號(使用網(wǎng)絡(luò)字節(jié)順序)
sin_addr DWORD ? ;IP地址(使用網(wǎng)絡(luò)字節(jié)順序)
sin_zero BYTE 8 dup (?) ;空字節(jié)
SOCKADDR_IN ends
sin_family在不同的操作系統(tǒng)下有不同的值,但winsock環(huán)境一般都固定為 AF_INET,也可以是PF_INET,在windows.inc
中被定義為與AF_INET同樣的值。sin_port 和 sin_addr 字段分別指定端口號和IP地址,放入這兩個字段的數(shù)據(jù)字節(jié)
順序必須是Internet順序。這到底是怎么一回事呢?學過匯編語言的朋友都知道,Intel的CPU對內(nèi)存操作數(shù)使用的是小尾
順序,這種方式是將位數(shù)高的字節(jié)排在內(nèi)存地址高的位置,而位數(shù)低的字節(jié)排在內(nèi)存的低地址處,例如 12345678h ,
如果以字類型來存儲,在內(nèi)存表示為 78h 56h 34h 12h。如果以雙字來存儲,表示為5678h 1234h。而其它的CPU如Motorola
的CPU使用的是大尾順序,這種順序剛好相反,低位數(shù)排在內(nèi)存的高位,高位數(shù)排在內(nèi)存的低位,例如 12345678h表示為
12h 34h 56h 78h,這種方式比較適合我們一般的看書習慣,都是從左到右一路看過去。
本來這些東東不關(guān)我們事的,但是當兩種不同CPU的主機通訊時就不行了,而Internet協(xié)議是一組開放的協(xié)議,需要在不同的
計算機平臺之間進行通信,所以在實現(xiàn)時不能包含與特定平臺有關(guān)的東西,即做到平臺無關(guān)性。這就需要采用一種共同的數(shù)據(jù)
排列順序才行,而Internet協(xié)議采用了大尾順序,所以凡是80X86平臺的winsock編程,當使用IP地址和端口號等參數(shù)時,都需要
將它們轉(zhuǎn)換為大尾順序。
好了,到了數(shù)據(jù)定義的內(nèi)容了,我們看看都定義了什么:
.data
szusg db "usage: conscan <hostname>",13,10,0 ;使用幫助
fmt db "%d OPEN",13,10,0 ;輸出時顯示打開的端口
sa SOCKADDR_IN <> ;使用connect函數(shù)時用到
wsa WSADATA <> ;使用WSAStartup函數(shù)時用到
sfd dd 0 ;保存Socket句柄
port dd 0 ;保存要掃描的端口
pl dd 21 ;這是程序?qū)⒁獟呙璧亩丝诹斜? dd 22
dd 23
dd 25
dd 80
dd 137
dd 350
dd 8080
dd 6667
dd 31337
dd 0
.data?
hostname db 2024 dup (?) ;用于存貯域名
buffer db 100 dup (?)
在.data段中,值得注意的是 定義了 一個 SOCKADDR_IN 類型的 sa變量,還有一個就是 WSADATA型的 wsa變量
WSADATA是用來初始化winsock庫的,在使用winsock函數(shù)之前必須先使用WSAStartup函數(shù)來裝入并初始化動態(tài)連接
庫WS2_32.dll。例如在代碼段中的 invoke WSAStartup,101h,addr wsa 即表示使用1.1版本的socket并初始化。
在端口列表pl方面,這種直接定義在程序內(nèi)部的方式缺少了靈活性,如果能將常掃描的端口存入一個文件里,需要
掃描的時候才讀入端口列表來掃描就好多了,注意,最后一個0是不能缺少的,實際上它表示端口列表的終止,而并
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -