www.pudn.com > asmping.zip > Ping.asm


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