VPN-сервис Android || Способен перехватывать пакеты, но не может выполнить разрешение DNSAndroid

Форум для тех, кто программирует под Android
Ответить Пред. темаСлед. тема
Anonymous
 VPN-сервис Android || Способен перехватывать пакеты, но не может выполнить разрешение DNS

Сообщение Anonymous »

Цель
  • Цель этой работы — найти способ создать прозрачный прокси-сервер, который будет регистрировать пакеты.
То, что происходит -
  • Я может захватить пакет и записать его в журнал.
  • Возможность отправить пакет в DNS-сокет.
  • Получение IP-адреса запрашиваемого домена.
То, где я застрял -
  • Но не могу отправить пакет на мое устройство через VPN, так как мои страницы не выдают ошибку с разрешением DNS.
Это мой полный код для захвата и регистрации моих пакеты.
package com.example.vpn
import android.net.VpnService
import android.os.ParcelFileDescriptor
import android.util.Log
import java.io.FileDescriptor
import java.io.FileInputStream
import java.io.FileOutputStream
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetAddress
import java.net.NetworkInterface
import java.net.SocketTimeoutException
import java.util.concurrent.SynchronousQueue
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit

class VpnNetworkException(message: String, cause: Throwable? = null) : Exception(message, cause)

class MyVpnService : VpnService(){
private var TAG = "SS"
private var vpnInterface: ParcelFileDescriptor? = null

override fun onCreate() {
super.onCreate()
startVpn()
}

private fun startVpn() {
val builder = Builder()
val ip = getLocalIpAddress()

Log.d("SS", "DeviceIP $ip")

builder.addAddress("192.168.30.221", 24)
builder.addDnsServer("8.8.8.8")
builder.addRoute("0.0.0.0",0 )
builder.setBlocking(true)

vpnInterface = builder.setSession("vpn").establish()

vpnInterface?.let {
Thread {
capturePackets(it.fileDescriptor)
}.start()
}
}

private fun capturePackets(fd: FileDescriptor) {
val input = FileInputStream(fd)
val buffer = ByteArray(32767)
val dnsSocket = DatagramSocket()
val executor = ThreadPoolExecutor(0, 32, 60L, TimeUnit.SECONDS, SynchronousQueue())
protect(dnsSocket)

while (true) {
val length = input.read(buffer)

if (length == 0) {
continue
}

val packetData = buffer.copyOf(length)

executor.execute {
Log.d("SS","Packet: ${packetData.toHexString()}")
parsePacket(packetData)
}
}
}

private fun ByteArray.toHexString(): String {
return joinToString(separator = " ") { byte -> "%02x".format(byte) }
}

private fun parsePacket(packetData: ByteArray) {
// Parse the IP Header (assuming IPv4)
val ipVersion = (packetData[0].toInt() shr 4) and 0xF

if (ipVersion == 4) {
Log.d("SS","IPv4 Packet Detected ${packetData.toHexString()}")

// Extract the source and destination IP addresses (IPv4)
val sourceIp = packetData.slice(12..15).toByteArray().toIpString()
val destinationIp = packetData.slice(16..19).toByteArray().toIpString()
Log.d("SS","Source IP: $sourceIp")
Log.d("SS","Destination IP: $destinationIp")

// Extract the Protocol (TCP/UDP/ICMP)
when (packetData[9].toInt()) {
6 -> { // TCP Protocol
Log.d("SS","TCP Packet Detected")
parseTcpHeader(packetData)
}
17 -> { // UDP Protocol
Log.d("SS","UDP Packet Detected")
parseUdpHeader(packetData, destinationIp)
}
else -> Log.d("SS","Other Protocol: ${packetData[9].toInt()}")
}
} else {
Log.d("SS","Non-IPv4 Packet")
}
}

// Parse TCP Header
private fun parseTcpHeader(packetData: ByteArray) {
// IP header length is in the lower 4 bits of the first byte (multiply by 4 to get byte size)
val ipHeaderLength = (packetData[0].toInt() and 0x0F) * 4

// TCP header starts right after the IP header
val tcpHeaderStart = ipHeaderLength
val sourcePort = (packetData[tcpHeaderStart].toInt() shl 8) or packetData[tcpHeaderStart + 1].toInt()
val destinationPort = (packetData[tcpHeaderStart + 2].toInt() shl 8) or packetData[tcpHeaderStart + 3].toInt()

Log.d("SS", "Source Port: $sourcePort")
Log.d("SS", "Destination Port: $destinationPort")
}

// Parse UDP Header
private fun parseUdpHeader(packetData: ByteArray, destinationIP: String) {
Log.w(TAG, "=================================== REQUEST ======================================")
// IP header length is in the lower 4 bits of the first byte (multiply by 4 to get byte size)
val ipHeaderLength = (packetData[0].toInt() and 0x0F) * 4
Log.d("SS", "IP header length $ipHeaderLength")

if (packetData.size udpPayload.size) {
Log.e("SS", "Label length exceeds UDP payload size. index: $index, length: $length, payload size: ${udpPayload.size}")
break
}

// Extract the domain label and add it to the list
val label = udpPayload.copyOfRange(index + 1, index + 1 + length)

domainParts.add(String(label, Charsets.US_ASCII)) // Convert byte array to string
index += length + 1 // Move index forward
}

val domainName = domainParts.joinToString(".")
Log.d("SS", "Querying for domain $domainName")

Log.w(TAG, "=================================== ******* ======================================")
// Sending packets to destination
sendUDPPackets(packetData, udpPayload, destinationIP, destinationPort)
}

// Extension function to convert bytes to an IPv4 string
private fun ByteArray.toIpString(): String {
return joinToString(separator = ".") { byte -> (byte.toInt() and 0xFF).toString() }
}

// get the local IP of the device
private fun getLocalIpAddress(): String {
try {
val interfaces = NetworkInterface.getNetworkInterfaces()
for (networkInterface in interfaces) {
val addresses = networkInterface.inetAddresses
for (address in addresses) {
// Exclude loopback addresses
if (!address.isLoopbackAddress && address is InetAddress) {
val ipAddress = address.hostAddress
// Check if the IP is IPv4 (ignore IPv6 addresses)
if (ipAddress.indexOf(':') < 0) {
return ipAddress
}
}
}
}
} catch (e: Exception) {
Log.e("SS", "Error getting IP address: ${e.message}")
}
return "192.168.30.1" // Return null if no IP address found
}

// send UDP packet to the destination
private fun sendUDPPackets(packet:ByteArray, udppayload: ByteArray, destinationIp:String, destinationPort:Int) {
val socket = DatagramSocket()
protect(socket)

try {
val dnsServer: InetAddress = InetAddress.getByName("8.8.8.8")
val dnsPacket = DatagramPacket(udppayload, udppayload.size, dnsServer, 53)
socket.send(dnsPacket)
Log.d("SS", "Packet sent to $destinationIp:$destinationPort")

// Set a timeout for receiving replies
socket.soTimeout = 10000 // Timeout after 2 seconds

// Attempt to receive a reply
val datagramData = ByteArray(1024)
val replyPacket = DatagramPacket(datagramData, datagramData.size)
socket.receive(replyPacket)
val response = datagramData.copyOf(replyPacket.length)

val hexResponse = response.joinToString(" ") { String.format("%02X", it) }
Log.i(TAG, "Received DNS response in hex: $hexResponse")

injectPacket(packet, response)
} catch (e: SocketTimeoutException) {
Log.e("SS", "No reply received within the timeout period.")
} catch (e: Exception) {
Log.e("SS", "Error sending UDP packet: ${e.message}")
} finally {
socket.close()
}
}

private fun injectPacket(packet: ByteArray, dnsResponse: ByteArray) {
val ipOutPacket = packet.copyOf()
// swap ipv4
//12,13,14,15
//16,17,18,19
var temp = ipOutPacket[12]
ipOutPacket[12] = ipOutPacket[16]
ipOutPacket[16] = temp

temp = ipOutPacket[13]
ipOutPacket[13] = ipOutPacket[17]
ipOutPacket[17] = temp

temp = ipOutPacket[14]
ipOutPacket[14] = ipOutPacket[18]
ipOutPacket[18] = temp

temp = ipOutPacket[15]
ipOutPacket[15] = ipOutPacket[19]
ipOutPacket[19] = temp

// swaping ports
// 20,21
// 22,23
val ipHeaderLength = (packet[0].toInt() and 0x0F) * 4
//
temp = ipOutPacket[ipHeaderLength]
ipOutPacket[ipHeaderLength] = ipOutPacket[ipHeaderLength+2]
ipOutPacket[ipHeaderLength+2] = temp

temp = ipOutPacket[ipHeaderLength+1]
ipOutPacket[ipHeaderLength+1] = ipOutPacket[ipHeaderLength+3]
ipOutPacket[ipHeaderLength+3] = temp

val newArray = ipOutPacket.sliceArray(0 until ipHeaderLength+8)
val outArray = newArray + dnsResponse

val totalBytes = outArray.size
Log.d("SS","New byte length $totalBytes")

outArray[3] = (totalBytes and 0xFF).toByte() // Lower byte
outArray[2] = (totalBytes shr 8).toByte() // Upper byte

val hexResponse = outArray.joinToString(" ") { String.format("%02X", it) }
Log.i(TAG, "ipOutPacket response in hex: $hexResponse")

parseDnsResponse(outArray)
try {
val output = FileOutputStream(vpnInterface!!.fileDescriptor)
output.write(outArray)
output.flush()
} catch (e: Exception) {
Log.e("VPN", "Error injecting packet: ${e.message}")
}
}

private fun parseDnsResponse(packetData:ByteArray) {
Log.w(TAG, "=================================== RESPONSE ======================================")
val ipHeaderLength = (packetData[0].toInt() and 0x0F) * 4
if (packetData.size udpPayload.size) {
Log.e("SS", "Label length exceeds UDP payload size. index: $index, length: $length, payload size: ${udpPayload.size}")
break
}

// Extract the domain label and add it to the list
val label = udpPayload.copyOfRange(index + 1, index + 1 + length)

domainParts.add(String(label, Charsets.US_ASCII)) // Convert byte array to string
index += length + 1 // Move index forward
}

val domainName = domainParts.joinToString(".")
Log.d("SS", "Querying for domain $domainName")

index += 5 // skip QTYPE (2 bytes) and QCLASS (2 bytes)

// Parse the answer section
for (i in 0 until answerRRCount) {
// Answer Name (can be a pointer)
val namePointer = ((udpPayload[index].toInt() and 0xFF) shl 8) or (udpPayload[index + 1].toInt() and 0xFF)
index += 2 // Move past the pointer

// Type (A, CNAME, etc.)
val type = ((udpPayload[index].toInt() and 0xFF) shl 8) or (udpPayload[index + 1].toInt() and 0xFF)
index += 2

// Class (Internet = 1)
val classType = ((udpPayload[index].toInt() and 0xFF) shl 8) or (udpPayload[index + 1].toInt() and 0xFF)
index += 2

// TTL (Time to live)
val ttl = ((udpPayload[index].toInt() and 0xFF) shl 24) or
((udpPayload[index + 1].toInt() and 0xFF) shl 16) or
((udpPayload[index + 2].toInt() and 0xFF) shl 8) or
(udpPayload[index + 3].toInt() and 0xFF)
index += 4

// Data length
val dataLength = ((udpPayload[index].toInt() and 0xFF) shl 8) or (udpPayload[index + 1].toInt() and 0xFF)
index += 2

// Data (IP address or CNAME)
val data: String = when (type) {
1 -> { // A record (IPv4 address)
val ip = "${udpPayload[index].toInt() and 0xFF}.${udpPayload[index + 1].toInt() and 0xFF}.${udpPayload[index + 2].toInt() and 0xFF}.${udpPayload[index + 3].toInt() and 0xFF}"
index += 4 // Move past the IP address
ip
}
5 -> { // CNAME record
val cnameParts = mutableListOf()
var cnameIndex = index
while (udpPayload[cnameIndex].toInt() != 0) {
val labelLength = udpPayload[cnameIndex].toInt() and 0xFF
val cnameLabel = udpPayload.copyOfRange(cnameIndex + 1, cnameIndex + 1 + labelLength)
cnameParts.add(String(cnameLabel, Charsets.US_ASCII))
cnameIndex += labelLength + 1
}
index = cnameIndex + 1 // Move past the CNAME
cnameParts.joinToString(".")
}
else -> {
"Unknown type"
}
}

Log.d("SS", "Answer #$i - Name: $namePointer, Type: $type, Class: $classType, TTL: $ttl, Data: $data")
}

Log.w(TAG, "=================================== ***** ======================================")
}

override fun onDestroy() {
super.onDestroy()
vpnInterface?.close()
}
}

Журналы:
W =================================== REQUEST ======================================
2024-09-28 19:31:57.244 5366-5550 SS com.example.vpn D IP header length 20
2024-09-28 19:31:57.245 5366-5550 SS com.example.vpn D UDP HEADER, 76 c5 00 35 00 28 07 75
2024-09-28 19:31:57.248 5366-5550 SS com.example.vpn D UDP PAYLOAD, 00 f5 01 00 00 01 00 00 00 00 00 00 03 77 77 77 06 72 65 64 64 69 74 03 63 6f 6d 00 00 01 00 01
2024-09-28 19:31:57.249 5366-5550 SS com.example.vpn D Source Port: 30405
2024-09-28 19:31:57.249 5366-5550 SS com.example.vpn D Destination Port: 53
2024-09-28 19:31:57.249 5366-5550 SS com.example.vpn D transaction id: 245
2024-09-28 19:31:57.249 5366-5550 SS com.example.vpn D flags: 0
2024-09-28 19:31:57.249 5366-5550 SS com.example.vpn D question count: 1
2024-09-28 19:31:57.249 5366-5550 SS com.example.vpn D answerRRCount: 0
2024-09-28 19:31:57.249 5366-5550 SS com.example.vpn D authorityRRCount: 0
2024-09-28 19:31:57.250 5366-5550 SS com.example.vpn D Querying for domain www.reddit.com
2024-09-28 19:31:57.250 5366-5550 SS com.example.vpn W =================================== ******* ======================================

=================================== RESPONSE ======================================
ipOutPacket response in hex: 45 00 00 9F 4A 30 40 00 40 11 00 EC 08 08 08 08 C0 A8 1E DD 00 35 76 C5 00 28 07 75 00 F5 81 80 00 01 00 05 00 00 00 00 03 77 77 77 06 72 65 64 64 69 74 03 63 6F 6D 00 00 01 00 01 C0 0C 00 05 00 01 00 00 13 05 00 17 06 72 65 64 64 69 74 03 6D 61 70 06 66 61 73 74 6C 79 03 6E 65 74 00 C0 2C 00 01 00 01 00 00 00 18 00 04 97 65 01 8C C0 2C 00 01 00 01 00 00 00 18 00 04 97 65 C1 8C C0 2C 00 01 00 01 00 00 00 18 00 04 97 65 41 8C C0 2C 00 01 00 01 00 00 00 18 00 04 97 65 81 8C
2024-09-28 19:31:57.341 5366-5551 SS com.example.vpn D UDP HEADER, 00 35 d5 5b 00 2e 10 c2
2024-09-28 19:31:57.341 5366-5489 SS com.example.vpn I Received DNS response in hex: E4 23 81 80 00 01 00 05 00 00 00 00 07 70 72 65 76 69 65 77 04 72 65 64 64 02 69 74 00 00 01 00 01 C0 0C 00 05 00 01 00 00 00 23 00 21 09 64 75 61 6C 73 74 61 63 6B 06 72 65 64 64 69 74 03 6D 61 70 06 66 61 73 74 6C 79 03 6E 65 74 00 C0 2D 00 01 00 01 00 00 00 17 00 04 97 65 41 8C C0 2D 00 01 00 01 00 00 00 17 00 04 97 65 C1 8C C0 2D 00 01 00 01 00 00 00 17 00 04 97 65 81 8C C0 2D 00 01 00 01 00 00 00 17 00 04 97 65 01 8C
2024-09-28 19:31:57.341 5366-5489 SS com.example.vpn D New byte length 170
2024-09-28 19:31:57.345 5366-5550 SS com.example.vpn D UDP PAYLOAD, 00 f5 81 80 00 01 00 05 00 00 00 00 03 77 77 77 06 72 65 64 64 69 74 03 63 6f 6d 00 00 01 00 01 c0 0c 00 05 00 01 00 00 13 05 00 17 06 72 65 64 64 69 74 03 6d 61 70 06 66 61 73 74 6c 79 03 6e 65 74 00 c0 2c 00 01 00 01 00 00 00 18 00 04 97 65 01 8c c0 2c 00 01 00 01 00 00 00 18 00 04 97 65 c1 8c c0 2c 00 01 00 01 00 00 00 18 00 04 97 65 41 8c c0 2c 00 01 00 01 00 00 00 18 00 04 97 65 81 8c
2024-09-28 19:31:57.345 5366-5550 SS com.example.vpn D Source Port: 53
2024-09-28 19:31:57.345 5366-5550 SS com.example.vpn D Destination Port: 30405
2024-09-28 19:31:57.345 5366-5550 SS com.example.vpn D transaction id: 245
2024-09-28 19:31:57.345 5366-5550 SS com.example.vpn D flags: 128
2024-09-28 19:31:57.345 5366-5550 SS com.example.vpn D question count: 1
2024-09-28 19:31:57.345 5366-5550 SS com.example.vpn D answerRRCount: 5
2024-09-28 19:31:57.345 5366-5550 SS com.example.vpn D authorityRRCount: 0
2024-09-28 19:31:57.345 5366-5550 SS com.example.vpn D Querying for domain www.reddit.com
2024-09-28 19:31:57.346 5366-5550 SS com.example.vpn D Answer #0 - Name: 49164, Type: 5, Class: 1, TTL: 4869, Data: reddit.map.fastly.net
2024-09-28 19:31:57.346 5366-5550 SS com.example.vpn D Answer #1 - Name: 49196, Type: 1, Class: 1, TTL: 24, Data: 151.101.1.140
2024-09-28 19:31:57.346 5366-5550 SS com.example.vpn D Answer #2 - Name: 49196, Type: 1, Class: 1, TTL: 24, Data: 151.101.193.140
2024-09-28 19:31:57.346 5366-5550 SS com.example.vpn D Answer #3 - Name: 49196, Type: 1, Class: 1, TTL: 24, Data: 151.101.65.140
2024-09-28 19:31:57.346 5366-5550 SS com.example.vpn D Answer #4 - Name: 49196, Type: 1, Class: 1, TTL: 24, Data: 151.101.129.140
2024-09-28 19:31:57.346 5366-5550 SS com.example.vpn W =================================== ***** ======================================

- As there are very limited resource on this any help will be appreciated.
- Have been stuck in this issue for a week now. 😢


Подробнее здесь: https://stackoverflow.com/questions/790 ... resolution
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение
  • Разница между VPN устройства (конфигурации VPN) и личной VPN на iOS?
    Anonymous » » в форуме IOS
    0 Ответы
    25 Просмотры
    Последнее сообщение Anonymous
  • Зачем перехватывать исключения в Java, если можно перехватывать Throwables?
    Anonymous » » в форуме JAVA
    0 Ответы
    26 Просмотры
    Последнее сообщение Anonymous
  • Перехватывать сетевые пакеты в пользовательское пространство и передавать пакеты приложению.
    Anonymous » » в форуме Linux
    0 Ответы
    33 Просмотры
    Последнее сообщение Anonymous
  • Как выполнить DNS-запрос к определенному DNS-серверу из PHP
    Anonymous » » в форуме Php
    0 Ответы
    52 Просмотры
    Последнее сообщение Anonymous
  • Как выполнить разрешение DNS в Android с Workmanager?
    Anonymous » » в форуме Android
    0 Ответы
    9 Просмотры
    Последнее сообщение Anonymous

Вернуться в «Android»