arping

  • 通过sendARP函数发送ARP广播查询实时监测目标MAC地址,若有变动立即输出警告。
    为应对复杂环境并实现快速判断,推荐结合Wireshark进行ARP报文分析或使用Linux版本
C:\Users\admin>certutil -hashfile D:\arping.exe sha256
SHA256 的 D:\arping.exe 哈希:
c8ec7f443b7e4d614c4a279fe30f4a2570ddf27da55ff1ab5080a3a752149d56

C:\Users\admin>certutil -hashfile D:\arping.exe sha1
SHA1 的 D:\arping.exe 哈希:
d7273f5532ffae64a98d79369bc19b53dc4b49d4

C:\Users\admin>certutil -hashfile D:\arping.exe md5
MD5 的 D:\arping.exe 哈希:
6a71419144bd84e0ae460afbec6c0dd3

⏬arping.exe

检测到冲突会提示信息

使用方法一

  • 直接双击arping.exe根据提示输入检测目标IP
  • 然后按Enter(回车)

alt text

使用方法二

  • arping.exe放入C:\Windows目录
  • 打开CMD(终端)输入arping <检测目标IP>
  • 然后按Enter(回车)

alt text

set HTTP_PROXY=http://192.168.123.2:10809
set HTTPS_PROXY=http://192.168.123.2:10809
go get golang.org/x/text/encoding/simplifiedchinese
go build

go.mod

module arping

go 1.21.4

require golang.org/x/sys v0.5.0

require golang.org/x/text v0.14.0 // indirect

go.sum

golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=

arping.go

package main

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"net"
	"os"
	"os/signal"
	"syscall"
	"time"
	"unsafe"
)

const redColor = "\033[31m"

func colorizeRed(text string) string {
	return fmt.Sprintf("%s%s\033[0m", redColor, text)
}

var lastSeenMac = make(map[string]string)

func main() {
	fmt.Println("arping 版本: V1.0.5 由 https://osed.cn 提供")

	targetIP := ""
	if len(os.Args) > 1 {
		targetIP = os.Args[1]
	}

	for {
		if targetIP == "" {
			fmt.Print("请输入IP: ")
			scanner := bufio.NewScanner(os.Stdin)
			if scanner.Scan() {
				targetIP = scanner.Text()
			}
			if targetIP == "" {
				fmt.Println("IP不能为空")
				continue
			}
		}

		// 创建一个通道用于通知goroutines立即退出
		exitSignalCh := make(chan struct{})

		// 创建一个新的goroutine来监听Ctrl+C
		go func() {
			c := make(chan os.Signal, 1)
			signal.Notify(c, os.Interrupt)
			<-c
			close(exitSignalCh) // 关闭通道,通知所有goroutine立即退出
			signal.Stop(c)      // 停止监听信号,这样可以重新开始监听
		}()

		arpCheckLoop(targetIP, exitSignalCh)

		// 重置targetIP以允许输入新的IP地址
		targetIP = ""
	}
}

func arpCheckLoop(targetIP string, exitSignalCh chan struct{}) {
	for {
		select {
		case <-exitSignalCh:
			return
		default:
			performArpCheck(targetIP)
			time.Sleep(500 * time.Millisecond) // 每500毫秒执行一次ARP查询
		}
	}
}

func performArpCheck(targetIP string) {
	macAddr, err := sendARP(targetIP)
	if err != nil {
		// 不再输出详细的错误信息,仅简单提示没有MAC地址
		fmt.Printf("%s 没有响应\n", targetIP)
		return
	}

	fmt.Printf("IP %s - MAC %s\n", targetIP, macAddr)
	previousMac, exists := lastSeenMac[targetIP]
	if exists && macAddr != previousMac {
		fmt.Printf(colorizeRed("发现IP冲突!IP:%s\n原MAC: %s\n现MAC: %s\n"), targetIP, previousMac, macAddr)
	}
	lastSeenMac[targetIP] = macAddr // 更新历史MAC地址
}

func sendARP(destIP string) (string, error) {
	dst := net.ParseIP(destIP).To4()
	if dst == nil {
		return "", fmt.Errorf("无效的IP地址")
	}

	src := make([]byte, 4) // 本机IP地址,0.0.0.0表示任意
	mac := make([]byte, 6) // 目标MAC地址
	maclen := uint32(len(mac))

	dll := syscall.MustLoadDLL("iphlpapi.dll")
	proc := dll.MustFindProc("SendARP")

	ret, _, callErr := proc.Call(
		uintptr(binary.LittleEndian.Uint32(dst)),
		uintptr(binary.LittleEndian.Uint32(src)),
		uintptr(unsafe.Pointer(&mac[0])),
		uintptr(unsafe.Pointer(&maclen)),
	)
	if ret != 0 {
		return "", fmt.Errorf("SendARP调用失败: %v", callErr)
	}

	return net.HardwareAddr(mac).String(), nil
}