VPN basierter Ad-Blocker für Android
Von Felix Kargl am 26.02.2026
Ich denke mal fast jeder verwendet heutzutage AdBlocker und falls nicht dann wird es langsam Zeit. Jedenfalls wollte ich mal verstehen wie das eigentlich so funktioniert und vor allem wie schwer oder leicht es ist einen eigenen AdBlocker zu bauen. Nachdem ich mich gerade bisschen mit Kotlin befasse, dachte ich mir das wäre ein gutes Projekt für die Masterklasse.
Was macht die App?
Es ist eine native Android App, die Werbung und Tracker auf DNS-Ebene blockiert und das ganze systemweit, für alle Apps auf dem Gerät. Die App braucht keine Root Rechte, sondern nur die VPN Permission. Ich verwende die VPNService Api von Android um DNS-Anfragen abzufangen und unerwünschte Domains mit einer NXDOMAIN (“Domain existiert nicht” Netzwerkantwort) zu beantworten.


DNS basiertes Blocking – Grundidee
Das Internet funktioniert im Hintergrund circa so:
- Du öffnest eine Website, z.B. youtube.com
- Dein Handy fragt einen DNS-Server: “Welche IP hat youtube.com?”
- Der DNS-Server antwortet: “142.250.80.46”
- Dein Browser verbindet sich mit dieser IP
Das Gleiche passiert auch für Ad-Domains wie ads.doubleclick.net.
- Eine App will ads.doubleclick.net laden
- Meine App fängt die DNS-Anfrage ab
- Statt die Anfrage weiterzuleiten, antwortet meine App: “Diese Domain existiert nicht” (NXDOMAIN)
- Die Werbung kann nicht geladen werden
Technisches Hauptelement – VpnServiceImplementation
Android verlangt eine explizite Zustimmung der Nutzer*In, bevor eine App den VpnService nutzen darf. Beim ersten Start erscheint ein System-Dialog: “Talos erlauben, eine VPN-Verbindung herzustellen?”
Anders als ein echter VPN leitet meine App NICHT den gesamten Traffic durch einen Tunnel. Es werden nur DNS Anfragen (Port 53) an den meinen festgelegten Upstream DNS Server (Cloudflare 1.1.1.1) geroutet. Der gesamte restliche Traffic läuft ganz normal, ohne Umweg. Dadurch gibt es auch keinen spürbaren Performance Verlust.
Sobald der VPN aktiv ist, läuft ein Hintergrund Thread, der eingehende Pakete über einen FileInputStream liest. Für jedes Paket wird geprüft:
- Ist es ein IPv4-UDP-Paket an Port 53
- Welche Domain wird angefragt?
- Steht die Domain auf der Blockliste?
Wenn ja wird eine NXDOMAIN Antwort zurückgeschickt. Wenn nein dann wird die DNS Anfrage an 1.1.1.1 weitergeleitet und eine echte Antwort zurückgegeben.
DNS Pakete parsen
IP-Header (20 Bytes) | UDP-Header (8 Bytes) | DNS-Daten
Die App prüft zuerst, ob es sich um ein IPv4-UDP-Paket handelt (Protokoll 17) und ob der Zielport 53 ist. Spannend ist dann das parsen des Domain Names. DNS codiert Domains nicht als einfachen String, sondern in einem speziellen Format:
“www.google.com” wird zu: [3]www[6]google[3]com[0]
Jedes Label wird mit seiner Länge als Byte eingeleitet. Eine 0 markiert das Ende. Die App liest dieses Format Byte für Byte und rekonstruiert daraus den Domain Namen.
Blocklist Management
Der BlocklistManager meiner App lädt sogenannte “Host-Files” aus dem Internet herunter, das sind fertige Listen von Werbe- und Tracker-Domains. Das Format ist simpel:
0.0.0.0 ads.example.com
0.0.0.0 tracker.facebook.com
etc.
Die erste Spalte wird ignoriert, relevant ist nur der Domain Name in Spalte zwei. Diese Domains werden in einem ConcurrentHashMap (einem thread-sicheren Set) gespeichert. Der Lookup ist damit O(1), egal ob 10 oder 500.000 Domains in der Liste stehen, die Prüfung dauert gleich lang. Zusätzlich habe ich in der App ein Subdomain-Blocking implementiert. Wenn “example.com” blockiert ist, wird automatisch auch “ads.example.com” etc. blockiert.
Fazit
Das ganze Projekt war eine große Challange und sehr spannend. Ich konnte mich in viele Bereiche einlesen von denen ich keine Ahnung hatte, um jetzt ein bisschen Ahnung zu haben.