├── .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
--------------------------------------------------------------------------------