├── .gitignore ├── LICENSE ├── README.md ├── config └── config.py.example ├── hmpa ├── __init__.py ├── email.py ├── oui.py ├── serverchan.py ├── tshark.py └── util.py ├── main.py ├── pics ├── email.jpg ├── monitor_mode.png └── raspberrypi.jpg ├── requirements.txt └── scripts └── raspberrypi-rtl8188.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | .idea 107 | .vscode 108 | .DS_Store 109 | 110 | config/config.py -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 神奇的战士 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hmpa-pi 2 | 3 | 在树莓派上,利用 Wireshark 扫描附近网络 WiFi 设备,并对扫描结果通过邮件或者微信进行推送。 4 | 5 | 临近春节回老家过年,家里没人,又不想安装摄像头监控,参考 [howmanypeoplearearound](https://github.com/schollz/howmanypeoplearearound) 写了一个监测脚本,当有手机或其它 Wi-Fi 设备在附近时,通过邮件或者微信提醒。 6 | 7 |

8 | 9 |

10 | 11 | ## 特性 12 | 13 | - [x] Wi-Fi 设备扫描 14 | - [x] 邮件提醒 15 | - [x] 微信提醒(Server 酱) 16 | - [ ] 陌生设备检测 17 | 18 | ## 原理 19 | 20 | 在 Wi-Fi 网络中,无线网卡是以广播模式发射信号的。当无线网卡将信息广播出去后,所有的设备都可以接收到该信息。将无线网卡设置为监听模式后,就可以捕获到该网卡接收范围的所有数据包。 21 | 通过这些数据包,就可以扫描出附近 Wi-Fi 的网络内的设备与信号强度。 22 | 23 |

24 | 25 |

26 | 27 | ## 监听模式的网卡 28 | 29 | 一些支持监听模式的网卡 30 | 31 | > [wifi-adapter-that-supports-monitor-mode](https://github.com/schollz/howmanypeoplearearound#wifi-adapter-that-supports-monitor-mode) 32 | 33 | - [USB Rt3070 $14](https://www.amazon.com/gp/product/B00NAXX40C/ref=as_li_tl?ie=UTF8&tag=scholl-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=B00NAXX40C&linkId=b72d3a481799c15e483ea93c551742f4) 34 | - [Panda PAU5 $14](https://www.amazon.com/gp/product/B00EQT0YK2/ref=as_li_tl?ie=UTF8&tag=scholl-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=B00EQT0YK2&linkId=e5b954672d93f1e9ce9c9981331515c4) 35 | - [Panda PAU6 $15](https://www.amazon.com/gp/product/B00JDVRCI0/ref=as_li_tl?ie=UTF8&tag=scholl-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=B00JDVRCI0&linkId=e73e93e020941cada0e64b92186a2546) 36 | - [Panda PAU9 $36](https://www.amazon.com/gp/product/B01LY35HGO/ref=as_li_tl?ie=UTF8&tag=scholl-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=B01LY35HGO&linkId=e63f3beda9855abd59009d6173234918) 37 | - [Alfa AWUSO36NH $33](https://www.amazon.com/gp/product/B0035APGP6/ref=as_li_tl?ie=UTF8&tag=scholl-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=B0035APGP6&linkId=b4e25ba82357ca6f1a33cb23941befb3) 38 | - [Alfa AWUS036NHA $40](https://www.amazon.com/gp/product/B004Y6MIXS/ref=as_li_tl?ie=UTF8&tag=scholl-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=B004Y6MIXS&linkId=0277ca161967134a7f75dd7b3443bded) 39 | - [Alfa AWUS036NEH $40](https://www.amazon.com/gp/product/B0035OCVO6/ref=as_li_tl?ie=UTF8&tag=scholl-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=B0035OCVO6&linkId=bd45697540120291a2f6e169dcf81b96) 40 | - [Sabrent NT-WGHU $15 (b/g) only](https://www.amazon.com/gp/product/B003EVO9U4/ref=as_li_tl?ie=UTF8&tag=scholl-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=B003EVO9U4&linkId=06d4784d38b6bcef5957f3f6e74af8c8) 41 | 42 | ## 软件安装 43 | 44 | ### Mac 45 | 46 | ```bash 47 | brew install wireshark 48 | brew cask install wireshark-chmodbpf 49 | ``` 50 | 51 | ### Linux 或 Raspberry Pi 52 | 53 | ```bash 54 | sudo apt-get install tshark 55 | 56 | # run as non-root 57 | 58 | sudo dpkg-reconfigure wireshark-common (select YES) 59 | sudo usermod -a -G wireshark ${USER:-root} 60 | newgrp wireshark 61 | ``` 62 | 63 | ## 配置网卡 64 | 65 | - 如果是支持监听模式的网卡,可以直接运行 66 | 67 | - 如果刚好在使用 `rtl8188 usb Wi-Fi 网卡` + Raspberry Pi ,需要先卸载 `rtl8192` 驱动,再加载 `rtl8188` 驱动 68 | 69 | ```bash 70 | #!/usr/bin/env bash 71 | uname -a 72 | 73 | # disable rtl8192 driver 74 | sudo depmod 4.14.79-v7+ 75 | sudo rmmod 8192cu 76 | sudo modprobe rtl8192cu 77 | 78 | # set RTL8188 monitor mode 79 | sudo ifconfig wlan1 down 80 | sudo iwconfig wlan1 mode monitor 81 | sudo ifconfig wlan1 up 82 | ``` 83 | 84 | ## 运行代码 85 | 86 | ### 下载代码 87 | ```bash 88 | git clone https://github.com/wangshub/hmpa-pi.git 89 | cd hmpa-pi/ && pip install -r requirements.txt 90 | 91 | ``` 92 | 93 | ### 编辑配置文件 94 | 95 | ```bash 96 | cp config/config.py.example config/config.py 97 | vi config/config.py 98 | ``` 99 | 100 | 参考配置 101 | 102 | ```python 103 | adapter = 'wlan1' 104 | 105 | use_email = True 106 | email = {"host": "smtp.163.com", 107 | "port": 465, 108 | "user": "xxxxxxx@163.com", 109 | "password": "xxxxxxxxxx", 110 | "to_user": "xxxxxxxx@xxxx.com"} 111 | 112 | use_wechat = True 113 | serverchan = {"sckey": "xxxxxxxxxxxxxxxxxxxxx"} 114 | 115 | 116 | known_devices = {"94:65:2d:xx:xx:xx": "my cellPhone", 117 | "dc:a4:ca:xx:xx:xx": "my Mac", 118 | "b8:27:eb:xx:xx:xx": "my raspberry"} 119 | ``` 120 | 121 | ### 运行 122 | 123 | ```bash 124 | python main.py 125 | ``` 126 | 127 | ## 消息推送 128 | 129 | - [Server 酱微信推送](http://sc.ftqq.com/3.version),需要 Github 登录获取 `sckey` 130 | - yagmail 邮箱推送 131 | 132 | ## 运行结果 133 | 134 | ```text 135 | 2019-01-24 07:37:01.211617 一共发现了 67 台设备 136 | 137 | Known Devices: 138 | - my cellPhone 139 | - my raspberry 140 | - my mac 141 | 142 | All Devices: 143 | - 00:e0:70:3e:xx:xx 14 DH TECHNOLOGY 144 | - 94:65:2d:91:xx:xx 14 OnePlus Technology (Shenzhen) Co., Ltd 145 | - dc:d9:16:7e:xx:xx -12 HUAWEI TECHNOLOGIES CO.,LTD 146 | - b8:27:eb:12:xx:xx -20 Raspberry Pi Foundation 147 | - 98:01:a7:eb:xx:xx -40 Apple, Inc. 148 | - 20:5d:47:44:xx:xx -44 vivo Mobile Communication Co., Ltd. 149 | - ac:b5:7d:5f:xx:xx -46 Liteon Technology Corporation 150 | - 04:03:d6:1f:xx:xx -47 Nintendo Co.,Ltd 151 | - d4:ee:07:55:xx:xx -48 HIWIFI Co., Ltd. 152 | - 44:6e:e5:63:xx:xx -51 HUAWEI TECHNOLOGIES CO.,LTD 153 | - 14:75:90:8d:xx:xx -51 TP-LINK TECHNOLOGIES CO.,LTD. 154 | - 34:96:72:1d:xx:xx -56 TP-LINK TECHNOLOGIES CO.,LTD. 155 | - d8:cb:8a:74:xx:xx -57 Micro-Star INTL CO., LTD. 156 | - 40:8d:5c:21:xx:xx -57 GIGA-BYTE TECHNOLOGY CO.,LTD. 157 | - 6c:59:40:25:xx:xx -58 SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. 158 | 159 | More ... 160 | ``` 161 | 162 | ## TODO 163 | 164 | - [ ] 美化打印信息 165 | - [ ] 更优雅的参数配置 166 | - [ ] 当发现新设备时提醒 167 | - [ ] 绘图统计 168 | - [ ] 设备距离估计 169 | 170 | ## 参考链接 171 | 172 | - [schollz / howmanypeoplearearound](https://github.com/schollz/howmanypeoplearearound) 173 | - [derv82 / wifite2](https://github.com/derv82/wifite2) 174 | - [第 3 章 监听 WiFi 网络](http://www.tup.tsinghua.edu.cn/upload/books/yz/067209-01.pdf) 175 | 176 | ## License 177 | 178 | - MIT 179 | - 仅供学习和研究,切勿非法使用 180 | -------------------------------------------------------------------------------- /config/config.py.example: -------------------------------------------------------------------------------- 1 | 2 | adapter = 'wlan1' 3 | 4 | use_email = True 5 | email = {"host": "smtp.163.com", 6 | "port": 465, 7 | "user": "xxxxxxx@163.com", 8 | "password": "xxxxxxxxxx", 9 | "to_user": "xxxxxxxx@xxxx.com"} 10 | 11 | use_wechat = True 12 | serverchan = {"sckey": "xxxxxxxxxxxxxxxxxxxxx"} 13 | 14 | 15 | known_devices = {"94:65:2d:xx:xx:xx": "my cellPhone", 16 | "dc:a4:ca:xx:xx:xx": "my Mac", 17 | "b8:27:eb:xx:xx:xx": "my raspberry"} -------------------------------------------------------------------------------- /hmpa/__init__.py: -------------------------------------------------------------------------------- 1 | from . import oui 2 | from . import tshark -------------------------------------------------------------------------------- /hmpa/email.py: -------------------------------------------------------------------------------- 1 | import yagmail 2 | 3 | 4 | class Email(object): 5 | def __init__(self, user, password, host, port): 6 | self.yag = yagmail.SMTP(user=user, 7 | password=password, 8 | host=host, 9 | port=port) 10 | 11 | def send(self, to_user, title, contents): 12 | self.yag.send(to_user, 13 | subject=title, 14 | contents=contents) -------------------------------------------------------------------------------- /hmpa/serverchan.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | 4 | API_URL = "https://sc.ftqq.com/{sckey}.send" 5 | 6 | 7 | def push(sckey, title, content=''): 8 | """push message to wechat 9 | 10 | :param sckey: serverchan sckey 11 | :param title: msg title (256B max) 12 | :param content: msg content (64kB max. support markdown) 13 | :return: response 14 | """ 15 | url = API_URL.format(sckey=sckey) 16 | try: 17 | response = requests.post(url, data={"text": title, "desp": content}) 18 | result = json.loads(response.text) 19 | return result 20 | except Exception as err: 21 | print(err) 22 | return None -------------------------------------------------------------------------------- /hmpa/tshark.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import platform 4 | import subprocess 5 | import time 6 | from datetime import datetime 7 | 8 | from . import oui 9 | 10 | 11 | def which(program): 12 | """Determines whether program exists 13 | """ 14 | 15 | def is_exe(fpath): 16 | return os.path.isfile(fpath) and os.access(fpath, os.X_OK) 17 | 18 | fpath, fname = os.path.split(program) 19 | if fpath: 20 | if is_exe(program): 21 | return program 22 | else: 23 | for path in os.environ["PATH"].split(os.pathsep): 24 | path = path.strip('"') 25 | exe_file = os.path.join(path, program) 26 | if is_exe(exe_file): 27 | return exe_file 28 | 29 | 30 | def _time_stamp(): 31 | return str(datetime.now()) 32 | 33 | 34 | def parse_mac_rssi(raw_output, sort=True): 35 | found_macs = {} 36 | found_devices = [] 37 | for line in raw_output.decode('utf-8').split('\n'): 38 | if line.strip() == '': 39 | continue 40 | 41 | dats = line.split() 42 | 43 | if len(dats) == 3: 44 | if ':' not in dats[0]: 45 | continue 46 | mac = dats[0] 47 | if mac not in found_macs: 48 | found_macs[mac] = [] 49 | rssi = float(dats[2]) 50 | found_macs[mac].append(rssi) 51 | 52 | for mac, rssi in found_macs.items(): 53 | found_macs[mac] = float(sum(rssi)) / float(len(rssi)) 54 | device = {'company': oui.info[mac[:8]] if mac[:8] in oui.info else 'Unknown', 55 | 'mac': mac, 56 | 'rssi': found_macs[mac]} 57 | found_devices.append(device) 58 | 59 | if sort: 60 | found_devices.sort(key=lambda x: x['rssi'], reverse=True) 61 | 62 | return {'time': _time_stamp(), 63 | 'found_devices': found_devices} 64 | 65 | 66 | def scan(adapter, scantime, sort=True): 67 | try: 68 | tshark = which("tshark") 69 | except: 70 | if platform.system() != 'Darwin': 71 | print('tshark not found, install using\n\napt-get install tshark\n') 72 | else: 73 | print('wireshark not found, install using: \n\tbrew install wireshark') 74 | print('you may also need to execute: \n\tbrew cask install wireshark-chmodbpf') 75 | return 76 | 77 | # Scan with tshark 78 | command = [tshark, '-I', '-i', adapter, '-a', 79 | 'duration:' + str(scantime), '-w', '/tmp/tshark-temp'] 80 | run_tshark = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 81 | stdout, _ = run_tshark.communicate() 82 | print(command) 83 | 84 | command = [ 85 | tshark, '-r', 86 | '/tmp/tshark-temp', '-T', 87 | 'fields', '-e', 88 | 'wlan.sa', '-e', 89 | 'wlan.bssid', '-e', 90 | 'radiotap.dbm_antsignal' 91 | ] 92 | run_tshark = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 93 | output, _ = run_tshark.communicate() 94 | devices = parse_mac_rssi(output) 95 | return devices 96 | 97 | 98 | if __name__ == '__main__': 99 | adapter = 'wlan1' 100 | scan(adapter, 10) -------------------------------------------------------------------------------- /hmpa/util.py: -------------------------------------------------------------------------------- 1 | def _diff(list_a, list_b): 2 | diff_a = list(set(list_a).difference(set(list_b))) 3 | diff_b = list(set(list_b).difference(set(list_a))) 4 | return diff_a, diff_b 5 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import time 3 | from datetime import datetime 4 | import schedule 5 | 6 | from hmpa import tshark 7 | from config import config 8 | from hmpa import serverchan 9 | from hmpa import email 10 | 11 | 12 | def brief_report(devices): 13 | all_devices = devices['found_devices'] 14 | title = "{time} 一共发现了 {sum} 台设备".format(time=devices['time'], 15 | sum=len(all_devices)) 16 | content = 'Known Devices: \n' 17 | 18 | for dev in all_devices: 19 | if dev['mac'] in list(config.known_devices.keys()): 20 | content += '- {name} \n'.format(name=config.known_devices[dev['mac']]) 21 | 22 | content += '\n\nAll Devices: \n' 23 | 24 | for dev in all_devices: 25 | content += '- {mac} {rssi} {company} \n'.format(mac=dev['mac'], 26 | rssi=int(dev['rssi']), 27 | company=dev['company']) 28 | return title, content 29 | 30 | 31 | def job(): 32 | adapter = config.adapter 33 | devices = tshark.scan(adapter, 60) 34 | title, content = brief_report(devices) 35 | print(content) 36 | 37 | if config.use_email: 38 | try: 39 | print('send email notification') 40 | mail = email.Email(config.email['user'], config.email['password'], config.email['host'], config.email['port']) 41 | mail.send(config.email['to_user'], title, content.split('\n')) 42 | except Exception as err: 43 | print(err) 44 | 45 | if config.use_wechat: 46 | try: 47 | print('send wechat notification') 48 | serverchan.push(config.serverchan['sckey'], title, content=content) 49 | except Exception as err: 50 | print(err) 51 | 52 | 53 | if __name__ == '__main__': 54 | schedule.every(1).minutes.do(job) 55 | while True: 56 | schedule.run_pending() 57 | time.sleep(1) 58 | -------------------------------------------------------------------------------- /pics/email.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangshub/hmpa-pi/a60a1cff0cdf334240abdfaf6613a285608c6e08/pics/email.jpg -------------------------------------------------------------------------------- /pics/monitor_mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangshub/hmpa-pi/a60a1cff0cdf334240abdfaf6613a285608c6e08/pics/monitor_mode.png -------------------------------------------------------------------------------- /pics/raspberrypi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangshub/hmpa-pi/a60a1cff0cdf334240abdfaf6613a285608c6e08/pics/raspberrypi.jpg -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.21.0 2 | schedule==0.6.0 3 | yagmail==0.11.214 4 | -------------------------------------------------------------------------------- /scripts/raspberrypi-rtl8188.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | uname -a 3 | 4 | # disable rtl8192 driver 5 | sudo depmod 4.14.79-v7+ 6 | sudo rmmod 8192cu 7 | sudo modprobe rtl8192cu 8 | 9 | # set monitor mode 10 | sudo ifconfig wlan1 down 11 | sudo iwconfig wlan1 mode monitor 12 | sudo ifconfig wlan1 up --------------------------------------------------------------------------------