-
Notifications
You must be signed in to change notification settings - Fork 213
/
Copy pathSniffer.kt
232 lines (207 loc) · 7.2 KB
/
Sniffer.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
package pubg.radar.sniffer
import org.pcap4j.core.*
import org.pcap4j.core.BpfProgram.BpfCompileMode.OPTIMIZE
import org.pcap4j.core.PcapNetworkInterface.PromiscuousMode.PROMISCUOUS
import org.pcap4j.packet.*
import pubg.radar.*
import pubg.radar.deserializer.proc_raw_packet
import pubg.radar.sniffer.SniffOption.*
import pubg.radar.util.notify
import java.io.*
import java.io.File.separator
import java.net.Inet4Address
import java.util.*
import javax.swing.*
import javax.swing.JOptionPane.*
import kotlin.collections.ArrayList
import kotlin.concurrent.thread
import kotlin.experimental.and
const val check1 = 12
const val check2 = 8
const val check3 = 4
const val flag1: Byte = 0
const val flag2: Byte = -1
fun Byte.check() = this == flag1 || this == flag2
class DevDesc(val dev: PcapNetworkInterface, val address: Inet4Address) {
override fun toString(): String {
return "[${address.hostAddress}] - ${dev.description}"
}
}
enum class SniffOption {
PortFilter,
PPTPFilter
}
val settingHome = "${System.getProperty("user.home")}$separator.pubgradar"
val settingFile = File("$settingHome${separator}setting.properties")
const val PROP_NetworkInterface = "NetworkInterface"
const val PROP_SniffOption = "SniffOption"
class Sniffer {
companion object: GameListener {
override fun onGameOver() {
}
val nif: PcapNetworkInterface
val localAddr: Inet4Address
val sniffOption: SniffOption
init {
register(this)
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())
} catch (e: Exception) {
e.printStackTrace()
}
var nif: PcapNetworkInterface? = null
var sniffOption: SniffOption? = null
val devs = Pcaps.findAllDevs()
val prop = Properties()
var found = false
try {
FileInputStream(settingFile).use {
prop.load(it)
val dev_desc = prop.getProperty(PROP_NetworkInterface)
sniffOption = SniffOption.valueOf(prop.getProperty(PROP_SniffOption))
nif = devs.first { it.description == dev_desc }
}
found = true
} catch (e: Exception) {
found = false
}
val choices = ArrayList<DevDesc>()
for (dev in devs)
dev.addresses
.filter { it.address is Inet4Address }
.mapTo(choices) { DevDesc(dev, it.address as Inet4Address) }
if (choices.isEmpty()) {
notify("No ipv4 network interface!!")
System.exit(-1)
}
val arrayChoices = choices.toTypedArray()
val msg = "Choose the network interface to sniff..."
val group = ButtonGroup()
val portFilter = JRadioButton("Direct network connection/virtual network adapter)",
!found || sniffOption == PortFilter)
val routeIpFilter = JRadioButton("PPTP tunneling",
found && sniffOption == PPTPFilter)
group.add(portFilter)
group.add(routeIpFilter)
val netDevs = JComboBox(arrayChoices)
if (found) netDevs.selectedItem = arrayChoices.firstOrNull { it.dev === nif }
val params = arrayOf(msg, netDevs, portFilter, routeIpFilter)
val option = showConfirmDialog(null, params, "Network interfaces", OK_CANCEL_OPTION)
if (option == CANCEL_OPTION)
System.exit(0)
val choice = netDevs.selectedIndex
if (choice == -1)
System.exit(-1)
val devDesc = arrayChoices[choice]
nif = devDesc.dev
val localAddr = devDesc.address
when {
portFilter.isSelected -> sniffOption = PortFilter
routeIpFilter.isSelected -> sniffOption = PPTPFilter
}
try {
File(settingHome).mkdirs()
FileOutputStream(settingFile).use {
if (devDesc.dev.description == null) return@use
prop.setProperty(PROP_NetworkInterface, devDesc.dev.description)
prop.setProperty(PROP_SniffOption, sniffOption!!.name)
prop.store(it, "network interface to sniff")
}
} catch (e: Exception) {
notify("Cannot save setting file to $settingFile")
}
this.nif = nif!!
this.localAddr = localAddr
this.sniffOption = sniffOption!!
}
const val snapLen = 65536
val mode = PROMISCUOUS
const val timeout = 1
const val PPTPFlag: Byte = 0b0011_0000
const val ACKFlag: Byte = 0b1000_0000.toByte()
fun ByteArray.toIntBE(pos: Int, num: Int): Int {
var value = 0
for (i in 0 until num)
value = value or ((this[pos + num - 1 - i].toInt() and 0xff) shl 8 * i)
return value
}
fun parsePPTPGRE(raw: ByteArray): Packet? {
var i = 0
if (raw[i] != PPTPFlag) return null//PPTP
i++
val hasAck = (raw[i] and ACKFlag) != 0.toByte()
i++
val protocolType = raw.toIntBE(i, 2)
i += 2
if (protocolType != 0x880b) return null
val payloadLength = raw.toIntBE(i, 2)
i += 2
val callID = raw.toIntBE(i, 2)
i += 2
val seq = raw.toIntBE(i, 4)
i += 4
if (hasAck) {
val ack = raw.toIntBE(i, 4)
i += 4
}
if (raw[i] != 0x21.toByte()) return null//not ipv4
i--
raw[i] = 0
val pppPkt = PppSelector.newPacket(raw, i, raw.size - i)
return pppPkt.payload
}
fun udp_payload(packet: Packet): UdpPacket? {
return when (sniffOption) {
PortFilter -> packet
PPTPFilter -> parsePPTPGRE(packet[IpV4Packet::class.java].payload.rawData)
}?.get(UdpPacket::class.java)
}
fun sniffLocationOnline() {
val handle = nif.openLive(snapLen, mode, timeout)
val filter = when (sniffOption) {
PortFilter -> "udp portrange 7000-7999"
PPTPFilter -> "ip[9]=47"
}
handle.setFilter(filter, OPTIMIZE)
thread(isDaemon = true) {
handle.loop(-1) { packet: Packet? ->
try {
packet!!
val ip = packet[IpPacket::class.java]
val udp = udp_payload(packet) ?: return@loop
val raw = udp.payload.rawData
if (ip.header.srcAddr == localAddr) {
if (udp.header.dstPort.valueAsInt() in 7000..7999)
proc_raw_packet(raw, false)
} else if (udp.header.srcPort.valueAsInt() in 7000..7999)
proc_raw_packet(raw, true)
} catch (e: Exception) {
}
}
}
}
fun sniffLocationOffline(): Thread {
return thread(isDaemon = true) {
val files = arrayOf("d:\\new05.pcap")
for (file in files) {
val handle = Pcaps.openOffline(file)
while (true) {
try {
val packet = handle.nextPacket ?: break
val ip = packet[IpPacket::class.java]
val udp = udp_payload(packet) ?: continue
val raw = udp.payload.rawData
if (ip.header.srcAddr == localAddr) {
if (udp.header.dstPort.valueAsInt() in 7000..7999)
proc_raw_packet(raw, false)
} else if (udp.header.srcPort.valueAsInt() in 7000..7999)
proc_raw_packet(raw,true)
} catch (e: Exception) {
}
Thread.sleep(1)
}
}
}
}
}
}