文章作者:罗云彬
DEBUG equ 0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Programmed by 罗云彬,
bigluo@telekbird.com.cn; Website:
http://asm.yeah.net; LuoYunBin's Win32 ASM page (罗云彬的编程乐园)
;
; 版权申明:
; 本程序可以自由传播,但引用时请保留作者信息
; Ver 1.0 --- 2001.11.11
;
; 编译环境: Masm32
; 1. ml /c /coff Ping.asm
; 2. Link /SUBSYSTEM:CONSOLE Ping.obj
;
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.386
.model flat, stdcall
option casemap :none ; case sensitive
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
include wsock32.inc
includelib wsock32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?
hStdIn dd ? ;控制台输入句柄
hStdOut dd ? ;控制台输出句柄
szHostName db 100 dup (?)
szBuffer db 1024 dup (?)
szBigBuffer db 65536 dup (?) ;接收 ICMP_REPLY 的大缓冲区
;********************************************************************
; 标志及命令行参数
;********************************************************************
dwTimeInterVal dd ? ;两个 Ping 之间的延时
dwOption dd ?
F_ABORT equ 0001h ;按了 Ctrl-C 终止
F_CONTINUE equ 0002h ; -t 参数
F_DELAY equ 0004h ; -d 参数
F_RESOLVE equ 0008h ; -a 参数
.data
szCopyRight db 0dh,0ah
db 'Ping by LuoYunBin',0dh,0ah
db '
http://asm.yeah.net, E-mail:
bigluo@telekbird.com.cn'
db 0dh,0ah,0ah,0
szHelp db 'Usage: ping [options] hostname',0dh,0ah,0ah
db 'Options:',0dh,0ah
db ' -t Ping the specified host until stopped.',0dh,0ah
db ' -a Resolve addresses to hostnames.',0dh,0ah
db ' -w timeout Timeout in milliseconds to wait for each reply.',0dh,0ah
db ' -d timeinterval Time in milliseconds to delay between ping hops.',0dh,0ah
db ' -n count Number of echo requests to send.',0dh,0ah
db ' -l size Send buffer size.',0dh,0ah
db 0dh,0ah
db 'example:',0dh,0ah
db ' ping 127.0.0.1',0dh,0ah
db ' ping
www.desthost.com -d 500 -t',0dh,0ah,0
szErrHost db 'Unknown host %s',0dh,0ah,0
szErrSocket db 'Socket error.',0dh,0ah,0
szErrTimeout db 'Request timed out.',0dh,0ah,0
szErrUnreach db 'Destination host unreachable.',0dh,0ah,0
szPinging db 'Ping %s [%s] with %d bytes of data:',0dh,0ah,0ah,0
szReply db 'Reply from %s: bytes=%d time=%dms TTL=%d',0dh,0ah,0
szStat db 0dh,0ah
db 'Ping statistics for %s:',0dh,0ah
db ' Packets: Sent = %d, Received = %d, Lost = %d (%d%% loss)',0dh,0ah
db 'Approximate round trip times in milli-seconds:',0dh,0ah
db ' Minimum = %dms, Maximum = %dms, Average = %dms',0dh,0ah,0
dwTimeOut dd 1000000 ;缺省超时时间 1 秒
dwPacketCount dd 4 ;缺省发送 4 个 ICMP 包
dwPacketSize dd 32 ;缺省包尺寸为 32 字节
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
if DEBUG
include Debug.asm
endif
include CmdLine.asm
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 控制台 Ctrl-C 捕获例程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_CtrlHandler proc _dwCtrlType
pushad
mov eax,_dwCtrlType
.if eax == CTRL_C_EVENT || eax == CTRL_BREAK_EVENT
or dwOption,F_ABORT
.endif
popad
mov eax,TRUE
ret
_CtrlHandler endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 控制台初始化
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ConsoleInit proc
invoke GetStdHandle,STD_INPUT_HANDLE
mov hStdIn,eax
invoke GetStdHandle,STD_OUTPUT_HANDLE
mov hStdOut,eax
invoke SetConsoleCtrlHandler,addr _CtrlHandler,TRUE
ret
_ConsoleInit endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 控制台输出子程序
; 注意: 用 WriteConsole 输出则执行时无法用 > 重定向到文件
; 用 WriteFile 则可以
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ConsolePrint proc _lpsz
local @dwCharWritten
pushad
invoke lstrlen,_lpsz
lea ecx,@dwCharWritten
invoke WriteFile,hStdOut,_lpsz,eax,ecx,NULL
popad
ret
_ConsolePrint endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 计算 ICMP 数据包的校验和
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_CalcCheckSum proc _lpsz,_dwSize
local @dwSize
pushad
mov ecx,_dwSize
shr ecx,1
xor ebx,ebx
mov esi,_lpsz
;********************************************************************
; 数据包校验和为每 16 位累加
;********************************************************************
cld
@@:
lodsw
movzx eax,ax
add ebx,eax
loop @B
;********************************************************************
; 最后如果有单 8 位则继续累加
;********************************************************************
mov ecx,_dwSize
or ecx,1
jz @F
lodsb
movzx eax,al
add ebx,eax
@@:
;********************************************************************
; 将高 16 位并入低 16 位后取反输出
;********************************************************************
mov eax,ebx
and eax,0ffffh
shr ebx,16
add eax,ebx
not ax
mov @dwSize,eax
popad
mov eax,@dwSize
ret
_CalcCheckSum endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Ping 主程序
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_Ping proc _lpszHostName
local @stWsa:WSADATA
local @stDest:sockaddr_in
local @stFrom:sockaddr_in
local @hSocket,@dwIPAddress,@dwSize
local @szBuffer[256]:byte
local @szIPAddress[256]:byte
local @stFdSet:fd_set
local @stTimeval:timeval
local @dwID,@dwSeq
local @dwCountSent,@dwCountRecv,@dwCountLost,@dwCountPer
local @dwTimeMin,@dwTimeMax,@dwTimeMul,@dwTimeAvg
pushad
xor eax,eax
mov @dwCountSent,eax ;一些统计用的变量
mov @dwCountRecv,eax
mov @dwCountPer,eax
mov @dwTimeMin,1000 * 1000
mov @dwTimeMax,eax
mov @dwTimeMul,eax
mov @dwTimeAvg,eax
;********************************************************************
; 初始化 socket
;********************************************************************
invoke WSAStartup,101h,addr @stWsa
.if eax
popad
ret
.endif
;********************************************************************
; 检查主机地址,如果是 IP 格式则检查合法性
; 如果是主机名格式则转换到 IP 地址
;********************************************************************
invoke inet_addr,_lpszHostName
.if eax == INADDR_NONE
invoke gethostbyname,_lpszHostName
.if eax != NULL
mov eax,[eax+12]
mov eax,[eax]
mov eax,[eax]
.else
invoke wsprintf,addr szBuffer,addr szErrHost,addr szHostName
invoke _ConsolePrint,addr szBuffer
jmp _Ping_Ret
.endif
.endif
mov @stDest.sin_addr,eax
mov @stDest.sin_port,0
mov @stDest.sin_family,AF_INET
invoke inet_ntoa,eax
.if eax != NULL
invoke lstrcpy,addr @szIPAddress,eax
.endif
;********************************************************************
; 初始化一个 socket 发送 ICMP 的 RAW 数据
;********************************************************************
invoke socket,AF_INET,SOCK_RAW,IPPROTO_ICMP
.if eax == INVALID_SOCKET
invoke _ConsolePrint,addr szErrSocket
jmp _Ping_Ret
.endif
mov @hSocket,eax
;********************************************************************
; 输入了 -a 参数则将 IP 地址反向转换到域名
;********************************************************************
.if dwOption & F_RESOLVE
invoke gethostbyaddr,addr @stDest.sin_addr,4,AF_INET
mov ecx,_lpszHostName
or eax,eax
jz @F
mov eax,[eax]
or eax,eax
jz @F
mov ecx,eax
@@:
.else
mov ecx,_lpszHostName
.endif
invoke wsprintf,addr szBuffer,addr szPinging,\
ecx,addr @szIPAddress,dwPacketSize
invoke _ConsolePrint,addr szBuffer
;********************************************************************
; 循环 Ping
;********************************************************************
xor ebx,ebx
mov @dwID,1
mov @dwSeq,1
.while TRUE
.break .if dwOption & F_ABORT ;控制台按了 Ctrl-C
.if ! (dwOption & F_CONTINUE) ;指定了 -t 参数则无限循环
.break .if ebx >= dwPacketCount
.endif
.if (dwOption & F_DELAY) && ebx ;指定了 -d 参数则等待一段时间
invoke Sleep,dwTimeInterVal
.endif
inc ebx
;********************************************************************
; 发送 Echo Request
;********************************************************************
assume esi:ptr icmp_hdr
mov esi,offset szBigBuffer
invoke RtlZeroMemory,esi,sizeof szBigBuffer
mov eax,@dwID
mov [esi].icmp_id,ax
mov eax,@dwSeq
mov [esi].icmp_seq,ax
inc @dwID
inc @dwSeq
mov [esi].icmp_type,ICMP_ECHOREQ ;构造 ICMP_ECHO_REQ 数据包
invoke GetTickCount
mov dword ptr [esi].icmp_data,eax ;将当前时间作为数据
mov ecx,dwPacketSize
add ecx,sizeof icmp_hdr - 1
invoke _CalcCheckSum,addr szBigBuffer,ecx
mov [esi].icmp_cksum,ax
inc @dwCountSent
invoke sendto,@hSocket,addr szBigBuffer,ecx,\
0,addr @stDest,sizeof sockaddr_in
.if eax == SOCKET_ERROR
invoke _ConsolePrint,addr szErrUnreach
.continue
.endif
assume esi:nothing
;********************************************************************
; 等待回复
;********************************************************************
mov @stFdSet.fd_count,1
push @hSocket
pop @stFdSet.fd_array
mov @stTimeval.tv_sec,0
push dwTimeOut ;等待超时的时间
pop @stTimeval.tv_usec
invoke select,1,addr @stFdSet,NULL,NULL,addr @stTimeval
.if eax == SOCKET_ERROR
invoke _ConsolePrint,addr szErrSocket
.continue
.endif
.if eax
;********************************************************************
; 接收返回数据包
;********************************************************************
mov @dwSize,sizeof @stFrom
invoke recvfrom,@hSocket,addr szBigBuffer,sizeof szBigBuffer,\
0,addr @stFrom,addr @dwSize
.if eax == SOCKET_ERROR
invoke _ConsolePrint,addr szErrSocket
.else
inc @dwCountRecv
mov eax,@stFrom.sin_addr ;返回地址
invoke inet_ntoa,eax
.if eax != NULL
invoke lstrcpy,addr @szBuffer,eax
.endif
invoke GetTickCount
sub eax,dword ptr szBigBuffer + sizeof ip_hdr + icmp_hdr.icmp_data
cmp eax,@dwTimeMin ;一些统计信息
jge @F
mov @dwTimeMin,eax
@@:
cmp eax,@dwTimeMax
jle @F
mov @dwTimeMax,eax
@@:
add @dwTimeMul,eax
movzx ecx,szBigBuffer + ip_hdr.ip_ttl
invoke wsprintf,addr szBuffer,addr szReply,\
addr @szBuffer,dwPacketSize,eax,ecx
invoke _ConsolePrint,addr szBuffer
.endif
.else
invoke _ConsolePrint,addr szErrTimeout
.endif
.endw
;********************************************************************
; 显示统计信息
;********************************************************************
mov eax,@dwCountSent
sub eax,@dwCountRecv
mov @dwCountLost,eax
mov ecx,100
mul ecx
mov ecx,@dwCountSent
.if ecx
div ecx
mov @dwCountPer,eax
.endif
mov eax,@dwTimeMul
xor edx,edx
mov ecx,@dwCountRecv
.if ecx
div ecx
mov @dwTimeAvg,eax
.else
mov @dwTimeMin,0
.endif
invoke wsprintf,addr szBuffer,addr szStat,\
addr @szIPAddress,\
@dwCountSent,@dwCountRecv,@dwCountLost,@dwCountPer,\
@dwTimeMin,@dwTimeMax,@dwTimeAvg
invoke _ConsolePrint,addr szBuffer
invoke closesocket,@hSocket
_Ping_Ret:
invoke WSACleanup
popad
ret
_Ping endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_Main proc
local @dwArgc
local @szBuffer[512]:byte
pushad
invoke _ConsolePrint,addr szCopyRight
invoke _argc
mov @dwArgc,eax
.if eax < 2
invoke _ConsolePrint,addr szHelp
popad
ret
.endif
;********************************************************************
; 处理命令行参数
;********************************************************************
mov ebx,1
.while ebx < @dwArgc
invoke _argv,ebx,addr @szBuffer,sizeof @szBuffer
mov ax,word ptr @szBuffer
.if al == '-' || al == '/'
.if ah == 't'
or dwOption,F_CONTINUE
.elseif ah == '?'
invoke _ConsolePrint,addr szHelp
popad
ret
.elseif ah == 'a'
or dwOption,F_RESOLVE
.elseif ah == 'w'
inc ebx
invoke _argv,ebx,addr @szBuffer,sizeof @szBuffer
invoke _GetStringValue,addr @szBuffer,NULL
.if eax > 10000
mov eax,10000
.endif
mov ecx,1000
mul ecx
mov dwTimeOut,eax
.elseif ah == 'd'
inc ebx
invoke _argv,ebx,addr @szBuffer,sizeof @szBuffer
invoke _GetStringValue,addr @szBuffer,NULL
.if eax > 10000
mov eax,10000
.endif
mov dwTimeInterVal,eax
or dwOption,F_DELAY
.elseif ah == 'n'
inc ebx
invoke _argv,ebx,addr @szBuffer,sizeof @szBuffer
invoke _GetStringValue,addr @szBuffer,NULL
.if ! eax
mov eax,4
.endif
mov dwPacketCount,eax
.elseif ah == 'l'
inc ebx
invoke _argv,ebx,addr @szBuffer,sizeof @szBuffer
invoke _GetStringValue,addr @szBuffer,NULL
.if eax < 4
mov eax,4
.endif
.if eax > 65500
mov eax,65500
.endif
mov dwPacketSize,eax
.endif
.else
invoke lstrcpy,addr szHostName,addr @szBuffer
.endif
inc ebx
.endw
.if szHostName
invoke _Ping,addr szHostName
.else
invoke _ConsolePrint,addr szHelp
.endif
popad
ret
_Main endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
call _ConsoleInit
call _Main
invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end start