1 |
root |
1.1 |
#!/usr/bin/perl |
2 |
|
|
|
3 |
|
|
use Event; |
4 |
|
|
use Time::HiRes qw(time); |
5 |
|
|
use Curses; |
6 |
|
|
use Socket; |
7 |
root |
1.7 |
use Storable; |
8 |
root |
1.1 |
|
9 |
root |
1.7 |
$DEV = "eth1"; |
10 |
|
|
$RAWDEV = "wifi0"; |
11 |
|
|
$PROC = "/proc/driver/aironet/$DEV"; |
12 |
root |
1.9 |
$STOREFILE = "wvsniff.dat"; |
13 |
root |
1.1 |
|
14 |
root |
1.12 |
my $GPS = eval { require GPSx; new GPS }; |
15 |
|
|
|
16 |
root |
1.1 |
$SIG{INT} = |
17 |
|
|
$SIG{TERM} = |
18 |
|
|
$SIG{HUP} = sub { exit }; |
19 |
|
|
|
20 |
|
|
sub config { |
21 |
|
|
open my $config, ">$PROC/Config" |
22 |
|
|
or die "$PROC/Config: $!"; |
23 |
|
|
print $config $_[0]; |
24 |
|
|
} |
25 |
|
|
|
26 |
|
|
config "Mode: yna\n"; |
27 |
|
|
system "ifconfig", $DEV, "up"; |
28 |
|
|
system "ifconfig", $RAWDEV, "up"; |
29 |
|
|
|
30 |
|
|
my $initscr; |
31 |
|
|
sub init_screen { |
32 |
|
|
initscr; |
33 |
|
|
$initscr = 1; |
34 |
|
|
start_color; |
35 |
|
|
cbreak; noecho; |
36 |
|
|
immedok 0; nonl; raw; intrflush 0; keypad 1; |
37 |
|
|
idlok 1; scrollok 0; leaveok 0; |
38 |
|
|
} |
39 |
|
|
|
40 |
|
|
sub end_win { |
41 |
|
|
endwin if $initscr--; |
42 |
|
|
} |
43 |
|
|
|
44 |
|
|
init_screen; |
45 |
|
|
|
46 |
root |
1.6 |
sub signal_quality { |
47 |
|
|
open my $stats, "<", "$PROC/Status" |
48 |
|
|
or return 0; |
49 |
|
|
sysread $stats, $buf, 1024; |
50 |
|
|
$buf =~ /Signal Quality: (\d+)/ && $1; |
51 |
|
|
} |
52 |
|
|
|
53 |
root |
1.1 |
my $refresh = Event->idle(nice => -5, parked => 1, min => 0.01, max => 1, repeat => 0, cb => sub { |
54 |
|
|
@menu = (); |
55 |
|
|
&$curmenu; |
56 |
|
|
|
57 |
|
|
$menu_idx += @menu if $menu_idx < 0; |
58 |
|
|
$menu_idx -= @menu if $menu_idx >= @menu; |
59 |
|
|
|
60 |
|
|
erase; |
61 |
|
|
|
62 |
root |
1.5 |
my $data = $GPS ? $GPS->data : {}; |
63 |
root |
1.2 |
|
64 |
root |
1.1 |
move 0, 0; |
65 |
root |
1.6 |
addstr $frame++ . ": " . signal_quality . " " . localtime($data->{time}) . " $data->{lat} $data->{long} $data->{alt}m"; |
66 |
root |
1.1 |
|
67 |
|
|
for my $idx (0 .. $#menu) { |
68 |
root |
1.3 |
move 2 + $idx, 0; |
69 |
root |
1.1 |
standout if $idx == $menu_idx; |
70 |
|
|
addstr $menu[$idx][0]; |
71 |
|
|
standend if $idx == $menu_idx; |
72 |
|
|
} |
73 |
|
|
|
74 |
root |
1.3 |
move 3 + scalar@menu, 0; |
75 |
root |
1.1 |
eval { |
76 |
|
|
$menu[$menu_idx][1]->(); |
77 |
|
|
}; |
78 |
|
|
|
79 |
|
|
refresh; |
80 |
|
|
}); |
81 |
|
|
|
82 |
root |
1.8 |
Event->timer(after => 0, interval => 2, cb => sub { $refresh->start }); |
83 |
|
|
|
84 |
root |
1.1 |
Event->io(fd => \*STDIN, poll => 'r', cb => sub { |
85 |
|
|
my $ch = getch; |
86 |
|
|
if ($ch eq "\x1b" or $ch eq "q") { |
87 |
|
|
Event::unloop; |
88 |
|
|
} elsif ($ch eq KEY_DOWN or $ch eq "j") { |
89 |
|
|
$menu_idx++; |
90 |
|
|
} elsif ($ch eq KEY_UP or $ch eq "k") { |
91 |
|
|
$menu_idx--; |
92 |
|
|
} else { |
93 |
|
|
eval { |
94 |
|
|
$menu[$menu_idx][2]->($ch); |
95 |
|
|
}; |
96 |
|
|
} |
97 |
|
|
$refresh->start; |
98 |
|
|
}); |
99 |
|
|
|
100 |
root |
1.7 |
-r $STOREFILE and |
101 |
|
|
$db = Storable::retrieve $STOREFILE; |
102 |
|
|
|
103 |
root |
1.1 |
END { |
104 |
root |
1.8 |
$db->{version} = 0; |
105 |
root |
1.7 |
Storable::store $db, $STOREFILE; |
106 |
root |
1.1 |
end_win; |
107 |
|
|
config "Mode: adhoc\n"; |
108 |
|
|
} |
109 |
|
|
|
110 |
|
|
sub decode_tags { |
111 |
|
|
my $pkt = shift; |
112 |
|
|
my %tag; |
113 |
|
|
|
114 |
|
|
while ($pkt) { |
115 |
|
|
my ($id, $len) = unpack "C C", $pkt; |
116 |
|
|
my $data = substr $pkt, 2, $len; |
117 |
|
|
$pkt = substr $pkt, 2 + $len; |
118 |
|
|
|
119 |
|
|
$id = ({ |
120 |
|
|
0 => SSID, |
121 |
|
|
1 => rates, |
122 |
|
|
2 => FH, |
123 |
|
|
3 => DS, |
124 |
|
|
4 => CF, |
125 |
|
|
5 => TIM, |
126 |
|
|
6 => IBSS, |
127 |
|
|
16 => challenge, |
128 |
|
|
})->{$id}; |
129 |
|
|
$tag{$id} = $data; |
130 |
|
|
} |
131 |
|
|
|
132 |
|
|
%tag; |
133 |
|
|
} |
134 |
|
|
|
135 |
|
|
sub PF_PACKET (){ 17 } |
136 |
|
|
sub SOCK_RAW (){ 3 } |
137 |
|
|
sub ETH_P_ALL (){ 0x0003 } |
138 |
|
|
sub SIOCGIFINDEX (){ 0x000008933 } |
139 |
|
|
|
140 |
|
|
sub open_pcap_socket { |
141 |
|
|
socket my $cap, PF_PACKET, SOCK_RAW, (unpack "s", pack "n", ETH_P_ALL) |
142 |
|
|
or die "unable to open packet socket: $!"; |
143 |
|
|
|
144 |
|
|
my $ifidx = pack "a16 I", $RAWDEV; |
145 |
|
|
ioctl $cap, SIOCGIFINDEX, $ifidx |
146 |
|
|
or die "can't get index of interface $RAWDEV: $!"; |
147 |
|
|
$ifidx = unpack "x16 I", $ifidx; |
148 |
|
|
|
149 |
root |
1.12 |
$::sockaddr = pack "S! S! I S! C C a8", PF_PACKET, (unpack "s", pack "n", ETH_P_ALL), $ifidx; |
150 |
|
|
bind $cap, $::sockaddr |
151 |
root |
1.1 |
or die "unable to bind to interface: $!"; |
152 |
|
|
|
153 |
|
|
$cap; |
154 |
|
|
} |
155 |
|
|
|
156 |
root |
1.12 |
sub escape($) { |
157 |
|
|
local $_ = $_[0]; |
158 |
|
|
s/([^\x20-\x7e])/sprintf "\\%03o", ord $1/ge; |
159 |
|
|
$_; |
160 |
|
|
} |
161 |
|
|
|
162 |
root |
1.1 |
sub e2h($) { |
163 |
|
|
join ":", map unpack("H*", $_), split //, $_[0]; |
164 |
|
|
} |
165 |
|
|
|
166 |
|
|
sub add_menu { |
167 |
|
|
my ($title, $display, $select) = @_; |
168 |
|
|
push @menu, [$title, $display, $select]; |
169 |
|
|
} |
170 |
|
|
|
171 |
|
|
$curmenu = \&menu_bss_list; |
172 |
|
|
|
173 |
|
|
sub bss2string { |
174 |
|
|
my $bss = shift; |
175 |
|
|
|
176 |
|
|
my $s = "AGE(" . int(time - $bss->{ts}) . ") " |
177 |
|
|
. "IP($bss->{ip}) " |
178 |
|
|
. "ARP($bss->{arp}) "; |
179 |
|
|
while (my ($k, $v) = each %{$bss->{ap}}) { |
180 |
root |
1.12 |
$s .= "(" . e2h($k) . " $v->{mode} '".escape($v->{essid})."') "; |
181 |
root |
1.1 |
} |
182 |
|
|
$s; |
183 |
|
|
} |
184 |
|
|
|
185 |
root |
1.5 |
sub show_map { |
186 |
|
|
my ($map, $w, $h) = @_; |
187 |
|
|
|
188 |
root |
1.6 |
my $data = $GPS->data; |
189 |
|
|
local $map->{"$data->{lat};$data->{long}"} = 0; |
190 |
|
|
|
191 |
root |
1.5 |
my @data = (("-" x $w) x $h); |
192 |
|
|
|
193 |
|
|
my ($x1, $y1, $x2, $y2) = (1e6, 1e6, 1e-6, 1e-6); |
194 |
root |
1.6 |
my $level = 1; |
195 |
root |
1.5 |
|
196 |
|
|
while (my ($k, $v) = each %$map) { |
197 |
|
|
my ($y, $x) = split /;/, $k; |
198 |
|
|
$x1 = $x if $x1 > $x; |
199 |
|
|
$x2 = $x if $x2 < $x; |
200 |
|
|
$y1 = $y if $y1 > $y; |
201 |
|
|
$y2 = $y if $y2 < $y; |
202 |
root |
1.6 |
$level = $v if $level < $v; |
203 |
root |
1.5 |
} |
204 |
|
|
|
205 |
|
|
$x2 = ($w - 1) / (($x2 - $x1) || 1e-6); |
206 |
|
|
$y2 = ($h - 1) / (($y2 - $y1) || 1e-6); |
207 |
|
|
|
208 |
|
|
while (my ($k, $v) = each %$map) { |
209 |
|
|
my ($y, $x) = split /;/, $k; |
210 |
|
|
|
211 |
|
|
$y = ($y - $y1) * $y2; |
212 |
|
|
$x = ($x - $x1) * $x2; |
213 |
|
|
|
214 |
root |
1.12 |
substr $data[$h-1-int $y], $x, 1, int($map->{$k} * 9 / $level); |
215 |
root |
1.5 |
} |
216 |
|
|
|
217 |
|
|
for (@data) { |
218 |
|
|
addstr $_ . "\n"; |
219 |
|
|
} |
220 |
|
|
} |
221 |
|
|
|
222 |
root |
1.1 |
sub display_bss { |
223 |
|
|
my $bss = shift; |
224 |
|
|
|
225 |
|
|
addstr "TS: $bss->{ts}\n"; |
226 |
root |
1.6 |
addstr "WEP/Weak packets received: $bss->{wep}/$bss->{weak}\n"; |
227 |
|
|
addstr "ARP/IP packets received: $bss->{arp}/$bss->{ip}\n"; |
228 |
root |
1.1 |
|
229 |
|
|
addstr "\naccess points\n"; |
230 |
|
|
|
231 |
|
|
while (my ($k, $v) = each %{$bss->{ap}}) { |
232 |
|
|
addstr " " . e2h($k) . "\n"; |
233 |
root |
1.12 |
addstr " mode $v->{mode}; channel $v->{channel}; essid '".escape($v->{essid})."'; beacon frames $v->{beacon}\n"; |
234 |
root |
1.1 |
} |
235 |
|
|
|
236 |
|
|
addstr "\nstations\n"; |
237 |
|
|
|
238 |
|
|
while (my ($k, $v) = each %{$bss->{station}}) { |
239 |
|
|
addstr " " . e2h($k); |
240 |
|
|
while (my ($k, $v) = each %$v) { |
241 |
|
|
addstr " " . (inet_ntoa $k) . "($v)"; |
242 |
|
|
} |
243 |
|
|
addstr "\n"; |
244 |
|
|
} |
245 |
root |
1.5 |
|
246 |
|
|
if ($bss->{gps}) { |
247 |
root |
1.6 |
my ($x, $y); getyx $y, $x; |
248 |
root |
1.5 |
addstr "\nmap\n"; |
249 |
root |
1.6 |
my $h = (&getmaxy - $y) - 3; |
250 |
|
|
show_map $bss->{gps}, int($h*1.8), $h; |
251 |
root |
1.5 |
} |
252 |
root |
1.1 |
} |
253 |
|
|
|
254 |
|
|
sub menu_bss_list { |
255 |
|
|
my @menu; |
256 |
root |
1.12 |
#for my $k (sort { int $db->{$b}{ts} <=> int $db->{$a}{ts} } keys %$db) { |
257 |
|
|
for my $k (keys %$db) { |
258 |
root |
1.8 |
my $v = $db->{$k}; |
259 |
root |
1.1 |
add_menu e2h($k) . " " . bss2string($v), |
260 |
|
|
sub { display_bss $v }, |
261 |
|
|
sub { |
262 |
|
|
if ($_[0] eq KEY_LEFT or $_[0] eq "h") { |
263 |
|
|
Event::unloop; |
264 |
|
|
} elsif ($_[0] eq KEY_RIGHT or $_[0] eq "l") { |
265 |
|
|
#$curmenu = sub { display_bss $k }; |
266 |
|
|
} |
267 |
root |
1.4 |
}; |
268 |
root |
1.1 |
} |
269 |
|
|
} |
270 |
|
|
|
271 |
|
|
sub activity { |
272 |
root |
1.5 |
if ($GPS) { |
273 |
|
|
my $data = $GPS->data; |
274 |
root |
1.6 |
$db->{$_[0]}{gps}{"$data->{lat};$data->{long}"} = signal_quality; |
275 |
root |
1.5 |
} |
276 |
root |
1.1 |
$db->{$_[0]}->{ts} = time; |
277 |
|
|
$refresh->start; |
278 |
|
|
} |
279 |
|
|
|
280 |
|
|
sub reg_ip { |
281 |
|
|
my ($bssid, $ip, $ether) = @_; |
282 |
|
|
$db->{$bssid}{station}{$ether}{$ip}++; |
283 |
|
|
activity $bssid; |
284 |
|
|
} |
285 |
|
|
|
286 |
|
|
{ |
287 |
|
|
my $cap = open_pcap_socket; |
288 |
|
|
my $pkt; |
289 |
root |
1.8 |
open LOG, ">/tmp/log";#d# |
290 |
root |
1.1 |
|
291 |
|
|
Event->io(fd => $cap, poll => 'r', cb => sub { |
292 |
root |
1.11 |
sysread $cap, $pkt, 120; |
293 |
root |
1.1 |
|
294 |
root |
1.8 |
printf LOG "%s\n", unpack "H*", $pkt; |
295 |
|
|
|
296 |
root |
1.1 |
my ($fc1, $fc2, $sid, $a1, $a2, $a3, $sc, $pkt) |
297 |
|
|
= unpack "C C n a6 a6 a6 S a*", $pkt; |
298 |
|
|
|
299 |
root |
1.12 |
send $cap, (pack "C C n a6 a6 a6 S a*", $fc1, $fc2, $sid, $a2, $a1, $a3, $sc, $pkt), 0, $::sockaddr; |
300 |
|
|
|
301 |
root |
1.8 |
my $a4; |
302 |
|
|
|
303 |
|
|
if ($fc2 & 0x03 == 0x03) { |
304 |
|
|
$a4 = substr $pkt, 0, 6; |
305 |
|
|
$pkt = substr $pkt, 6; |
306 |
|
|
} |
307 |
root |
1.1 |
|
308 |
|
|
if ($fc1 == 0x80 or $fc1 == 0x50) { |
309 |
|
|
my ($ts1, $ts2, $bi, $cf, $pkt) |
310 |
|
|
= unpack "L L S S a*", $pkt; |
311 |
|
|
|
312 |
|
|
my %tag = decode_tags $pkt; |
313 |
|
|
|
314 |
|
|
my $ap = $db->{$a3}{ap}{$a2} ||= {}; |
315 |
|
|
$ap->{mode} = ($cf & 3) == 1 ? "AP" : "adhoc"; |
316 |
root |
1.8 |
|
317 |
root |
1.10 |
# ESSID ' ' workaround for "closed-network feature" |
318 |
root |
1.8 |
$ap->{essid} = $tag{SSID} unless $ap->{essid} && $tag{SSID} eq " "; |
319 |
root |
1.1 |
$ap->{channel} = ord $tag{DS}; |
320 |
|
|
$ap->{beacon}++; |
321 |
|
|
|
322 |
|
|
activity $a3; |
323 |
|
|
} elsif ($fc1 == 0x08) { |
324 |
root |
1.8 |
my $bssid = ($a3, $a1, $a2, $a2)[$fc2 & 3]; |
325 |
root |
1.1 |
|
326 |
root |
1.5 |
if ($fc2 & 0x40) { |
327 |
root |
1.6 |
my ($iv1, $iv2, $iv3, $ivopt, $byte) = unpack "C C C C C", $pkt; |
328 |
root |
1.5 |
$db->{$bssid}{wep}++; |
329 |
root |
1.6 |
if ($iv1 > 2 and $iv1 < 14 and $iv2 == 255) { |
330 |
|
|
$db->{$bssid}{weak}++; |
331 |
|
|
} |
332 |
root |
1.5 |
} else { |
333 |
|
|
my ($llc, $et, $pkt) = unpack "a6 n a*", $pkt; |
334 |
root |
1.8 |
if ($llc eq "\xaa\xaa\x03\x00\x00\x00" |
335 |
|
|
or $llc eq "\xaa\xaa\x03\x00\x00\xf8") { # SNAP/UI/{ENCAP_ETHER,CISCO} |
336 |
root |
1.5 |
if ($et == 0x0800) { |
337 |
|
|
my ($vhl, $tos, $len, $id, $off, $ttl, $prot, $sum, $src, $dst, $pkt) |
338 |
|
|
= unpack "C C n n n C C n a4 a4 a*", $pkt; |
339 |
|
|
if ($vhl & 0x40 == 0x40) { |
340 |
|
|
$db->{$bssid}{ip}++; |
341 |
root |
1.8 |
reg_ip $bssid, $src, ($a2, $a2, $a3, $a4)[$fc2 & 3]; |
342 |
|
|
reg_ip $bssid, $dst, ($a1, $a3, $a1, $a3)[$fc2 & 3]; |
343 |
root |
1.5 |
} |
344 |
|
|
} elsif ($et == 0x0806) { |
345 |
|
|
my ($hrd, $pro, $hln, $pln, $op, $src_hw, $src, $dst_hw, $dst) |
346 |
|
|
= unpack "n n C C n a6 a4 a6 a4", $pkt; |
347 |
|
|
if ($hrd == 1 and $hln == 6 and $pln == 4) { |
348 |
|
|
reg_ip $bssid, $src, $src_hw; |
349 |
|
|
reg_ip $bssid, $dst, $dst_hw if $op != 1; |
350 |
|
|
$db->{$bssid}{arp}++; |
351 |
|
|
} |
352 |
root |
1.1 |
} |
353 |
root |
1.5 |
} else { |
354 |
|
|
0 && |
355 |
|
|
printf "?? %02x %02x %04x %04x: %s.%s \n", $fc1, $fc2, $sid, $sc, (unpack "H*", $llc), unpack "H*", $pkt; |
356 |
root |
1.1 |
} |
357 |
|
|
} |
358 |
|
|
} elsif ($fc1 != 0x40 and $fc1 != 0x10 and $fc1 != 0x00 and $fc1 != 0xb0) { |
359 |
|
|
0 && |
360 |
|
|
printf "%02x %02x %04x %04x: %s \n", $fc1, $fc2, $sid, $sc, unpack "H*", $pkt; |
361 |
|
|
} |
362 |
|
|
}); |
363 |
|
|
} |
364 |
|
|
|
365 |
|
|
Event::loop; |
366 |
|
|
|
367 |
|
|
|