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