Skip to content

Commit 6c65b22

Browse files
committed
DNS malware
1 parent 54381da commit 6c65b22

File tree

5 files changed

+182
-6
lines changed

5 files changed

+182
-6
lines changed

src/wireguard/dns-malware.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright (c) 2022 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package main
18+
19+
// #include <android/log.h>
20+
import "C"
21+
22+
import (
23+
"fmt"
24+
"strings"
25+
26+
"github.com/miekg/dns"
27+
)
28+
29+
30+
// parseDNSResponse parses the raw DNS response from a byte slice.
31+
func parseDNSResponse(data []byte) *dns.Msg {
32+
tag := cstring("WireGuard/GoBackend/parseDNSResponse")
33+
// Create a new DNS message structure
34+
dnsMsg := new(dns.Msg)
35+
36+
// Unpack the DNS message from the raw data
37+
err := dnsMsg.Unpack(data)
38+
if err != nil {
39+
C.__android_log_write(C.ANDROID_LOG_DEBUG, tag, cstring(fmt.Sprintf("Failed to unpack DNS message: %v", err)))
40+
return nil
41+
}
42+
43+
// Check if this is a DNS response (QR bit set)
44+
if dnsMsg.Response {
45+
return dnsMsg
46+
}
47+
48+
// If not a response, return nil
49+
return nil
50+
}
51+
52+
53+
/*
54+
* WasDNSMalwareBlocked checks if the DNS packet contains a "blocked:m" TXT
55+
* record under the "explanation.invalid" domain. If such a record is found,
56+
* it returns true along with the domain being blocked.
57+
* That record is found when our the DDG (malware) DNS server blocks domains that
58+
* serve malware
59+
*/
60+
func WasDNSMalwareBlocked(dnsData []byte) (bool, string) {
61+
dnsResponse := parseDNSResponse(dnsData)
62+
if dnsResponse == nil {
63+
return false, ""
64+
}
65+
66+
// Look for "explanation.invalid" in the additional section
67+
for _, rr := range dnsResponse.Extra {
68+
if txtRecord, ok := rr.(*dns.TXT); ok {
69+
if txtRecord.Hdr.Name == "explanation.invalid." {
70+
for _, txt := range txtRecord.Txt {
71+
if txt == "blocked:m" {
72+
// If there is a matching TXT record, return the blocked domain
73+
if len(dnsResponse.Question) > 0 {
74+
blockedDomain := dnsResponse.Question[0].Name
75+
// remove the trailing dot (FQDN) if present
76+
blockedDomain = strings.TrimSuffix(blockedDomain, ".")
77+
return true, blockedDomain
78+
}
79+
}
80+
}
81+
}
82+
}
83+
}
84+
85+
return false, ""
86+
}

src/wireguard/go.mod

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@ module golang.zx2c4.com/wireguard/android
33
go 1.18
44

55
require (
6-
golang.org/x/net v0.10.0
7-
golang.org/x/sys v0.8.0
6+
golang.org/x/net v0.27.0
7+
golang.org/x/sys v0.22.0
88
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b
99
)
1010

1111
require (
12-
golang.org/x/crypto v0.9.0 // indirect
12+
github.com/miekg/dns v1.1.62 // indirect
13+
golang.org/x/crypto v0.25.0 // indirect
14+
golang.org/x/mod v0.18.0 // indirect
15+
golang.org/x/sync v0.7.0 // indirect
16+
golang.org/x/tools v0.22.0 // indirect
1317
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
1418
)

src/wireguard/go.sum

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
11
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
2+
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
3+
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
24
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
35
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
6+
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
7+
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
8+
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
9+
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
410
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
511
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
12+
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
13+
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
14+
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
15+
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
616
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
717
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
18+
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
19+
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
820
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
21+
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
22+
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
923
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
1024
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
1125
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b h1:J1CaxgLerRR5lgx3wnr6L04cJFbWoceSK9JWBdglINo=

src/wireguard/jni.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ extern char *wgVersion();
1818
static JavaVM *JVM = NULL;
1919
static jobject GO_BACKEND = NULL;
2020
static jmethodID MID_SHOULD_ALLOW = NULL;
21+
static jmethodID MID_RECORD_MALWARE_BLOCK = NULL;
2122
static jint SDK = 0;
2223

2324
JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOn(JNIEnv *env, jobject goBackend, jstring ifname,
@@ -39,6 +40,7 @@ JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOn(JNI
3940
jclass clsGoBackend = (*env)->GetObjectClass(env, goBackend);
4041
const char *shouldAllowSig = "(ILjava/lang/String;ILjava/lang/String;ILjava/lang/String;I)Z";
4142
MID_SHOULD_ALLOW = jniGetMethodID(env, clsGoBackend, "shouldAllow", shouldAllowSig);
43+
MID_RECORD_MALWARE_BLOCK = jniGetMethodID(env, clsGoBackend, "recordMalwareBlock", "(Ljava/lang/String;)V");
4244
(*env)->DeleteLocalRef(env, clsGoBackend);
4345

4446
// Call Go method
@@ -137,6 +139,31 @@ int is_pkt_allowed(const uint8_t *buffer, int length) {
137139
return 1;
138140
}
139141

142+
int record_malware_block(const char *domain) {
143+
if (MID_RECORD_MALWARE_BLOCK == NULL) {
144+
log_print(PLATFORM_LOG_PRIORITY_ERROR, "MID_RECORD_MALWARE_BLOCK method not found");
145+
return 1;
146+
}
147+
148+
JNIEnv *env;
149+
jint rs = (*JVM)->AttachCurrentThread(JVM, &env, NULL);
150+
if (rs != JNI_OK) {
151+
log_print(PLATFORM_LOG_PRIORITY_ERROR, "Could not attach to JVM thread");
152+
return 1;
153+
}
154+
155+
// Prep call to Kotlin
156+
jstring jdomain = (*env)->NewStringUTF(env, domain);
157+
(*env)->CallVoidMethod(env, GO_BACKEND, MID_RECORD_MALWARE_BLOCK, jdomain);
158+
jniCheckException(env);
159+
160+
// cleanup
161+
(*env)->DeleteLocalRef(env, jdomain);
162+
163+
return 0;
164+
165+
}
166+
140167
JNIEXPORT void JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOff(JNIEnv *env, jclass c, jint handle)
141168
{
142169
wgTurnOff(handle);

src/wireguard/tun_android.go

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ Implementation of the TUN device interface for Android (wraps linux one)
2222

2323

2424
// #include <android/log.h>
25+
// #include <stdlib.h> // For C.free and C string functions
2526
// extern int is_pkt_allowed(char *buffer, int length);
2627
// extern int wg_write_pcap(char *buffer, int length);
28+
// extern int record_malware_block(const char *domain);
2729
import "C"
2830

2931
import (
@@ -50,10 +52,53 @@ func (tunWrapper *NativeTunWrapper) Name() (string, error) {
5052
return tunWrapper.nativeTun.Name()
5153
}
5254

53-
func (tunWrapper *NativeTunWrapper) Write(buf [][]byte, offset int) (int, error) {
54-
pktLen, err := tunWrapper.nativeTun.Write(buf, offset)
55+
func (tunWrapper *NativeTunWrapper) Write(bufs [][]byte, offset int) (int, error) {
56+
tag := cstring("WireGuard/GoBackend/Write")
5557

56-
// tag := cstring("WireGuard/GoBackend/Write")
58+
for _, buf := range bufs {
59+
// Check if it's an IPv4 packet
60+
if len(buf) <= offset {
61+
C.__android_log_write(C.ANDROID_LOG_DEBUG, tag, cstring("Skipping invalid packet, too short"))
62+
continue
63+
}
64+
switch buf[offset] >> 4 {
65+
case ipv4.Version:
66+
if len(buf) < ipv4.HeaderLen {
67+
C.__android_log_write(C.ANDROID_LOG_DEBUG, tag, cstring("Skipping bad IPv4 packet"))
68+
continue
69+
}
70+
71+
// Check if it's a UDP packet
72+
protocol := buf[offset+9]
73+
if protocol == 0x11 { // UDP
74+
// Extract the ports (skip IP and check transport layer headers)
75+
srcPort := (uint16(buf[offset+ipv4.HeaderLen]) << 8) | uint16(buf[offset+ipv4.HeaderLen+1])
76+
dstPort := (uint16(buf[offset+ipv4.HeaderLen+2]) << 8) | uint16(buf[offset+ipv4.HeaderLen+3])
77+
78+
if srcPort == 53 || dstPort == 53 {
79+
// Extract the DNS data (skip IP and UDP headers)
80+
dnsData := buf[offset+ipv4.HeaderLen+8:]
81+
82+
// Call the helper function to check if the DNS packet should be blocked
83+
shouldBlock, blockedDomain := WasDNSMalwareBlocked(dnsData)
84+
if shouldBlock {
85+
logMessage := "DNS malware was blocked for domain: " + blockedDomain + " due to 'blocked:m' TXT record"
86+
C.__android_log_write(C.ANDROID_LOG_DEBUG, tag, cstring(logMessage))
87+
88+
// call back into JVM and let the packet flow normally
89+
cBlockedDomain := C.CString(blockedDomain)
90+
defer C.free(unsafe.Pointer(cBlockedDomain))
91+
C.record_malware_block(cBlockedDomain) // ignore return code
92+
}
93+
}
94+
}
95+
default:
96+
// Not an IPv4 packet
97+
C.__android_log_write(C.ANDROID_LOG_DEBUG, tag, cstring("Invalid IP"))
98+
}
99+
}
100+
101+
pktLen, err := tunWrapper.nativeTun.Write(bufs, offset)
57102

58103
// PCAP recording
59104
// pcap_res := int(C.wg_write_pcap((*C.char)(unsafe.Pointer(&buf[offset])), C.int(pktLen+offset)))

0 commit comments

Comments
 (0)