Add DPI and malware detection tests

This commit is contained in:
emanuele-f 2022-01-20 20:07:13 +01:00
parent 44cda96718
commit a59664415a
11 changed files with 233 additions and 17 deletions

View File

@ -254,18 +254,13 @@ bool blacklist_match_ip(blacklist_t *bl, const zdtun_ip_t *ip, int ipver) {
/* ******************************************************* */
bool blacklist_match_ipstr(blacklist_t *bl, const char *ip_str) {
ndpi_ip_addr_t addr;
int ipver = ndpi_parse_ip_string(ip_str, &addr);
zdtun_ip_t ip;
zdtun_ip_t parsed;
if(ipver == 4)
ip.ip4 = addr.ipv4;
else if(ipver == 6)
memcpy(&ip.ip6, &addr.ipv6, 16);
else
int ipver = zdtun_parse_ip(ip_str, &parsed);
if(ipver < 0)
return false;
return blacklist_match_ip(bl, &ip, ipver);
return blacklist_match_ip(bl, &parsed, ipver);
}
/* ******************************************************* */

View File

@ -14,10 +14,9 @@ include_directories(${ROOTDIR}/submodules/zdtun)
include_directories(${ROOTDIR}/submodules/nDPI/src/include)
include(CTest)
list(APPEND CMAKE_CTEST_ARGUMENTS "--output-on-failure")
# Target to run tests and build them if necessary
add_custom_target(run_tests COMMAND ${CMAKE_CTEST_COMMAND})
add_custom_target(run_tests COMMAND CTEST_OUTPUT_ON_FAILURE=1 ${CMAKE_CTEST_COMMAND})
# build_tests(target)
macro(build_tests)
@ -30,5 +29,9 @@ endmacro()
build_tests(pcap_reader)
add_test(NAME dpi_extract COMMAND ./dpi extract)
build_tests(dpi)
add_test(NAME blacklist_match COMMAND ./blacklist match)
add_test(NAME blacklist_detection COMMAND ./blacklist detection)
build_tests(blacklist)

View File

@ -51,9 +51,73 @@ static void test_match() {
/* ******************************************************* */
static void detection_cb(pcapdroid_t *pd) {
conn_and_tuple_t *conn;
// IP blacklist
conn = assert_conn(pd, IPPROTO_ICMP, "1.1.1.1", 0, NULL);
assert1(conn->data->blacklisted_ip);
conn = assert_conn(pd, IPPROTO_TCP, "216.58.208.164", 80, NULL);
assert1(conn->data->blacklisted_ip);
conn = assert_conn(pd, IPPROTO_TCP, "2c9b:a9b9:83dd:d9d1::2003", 443, NULL);
assert1(conn->data->blacklisted_ip);
// Host blacklist
conn = assert_conn(pd, IPPROTO_UDP, "8.8.8.8", 53, "www.google.it");
assert0(conn->data->blacklisted_ip);
assert1(conn->data->blacklisted_domain);
conn = assert_conn(pd, IPPROTO_TCP, "146.112.255.155", 80, "www.internetbadguys.com");
assert1(conn->data->blacklisted_domain);
conn = assert_conn(pd, IPPROTO_TCP, "3a5d:15fe:e3cb:9c5f::2003", 443, "www.google.it");
assert0(conn->data->blacklisted_ip);
assert1(conn->data->blacklisted_domain);
// Whitelist
conn = assert_conn(pd, IPPROTO_TCP, "149.202.95.241", 80, "f-droid.org");
assert0(conn->data->blacklisted_domain);
assert0(conn->data->blacklisted_ip);
conn = assert_conn(pd, IPPROTO_TCP, "2ed5:9050:81e9:4b68:248:1893:25c8:1946", 443, "example.org");
assert0(conn->data->blacklisted_domain);
assert0(conn->data->blacklisted_ip);
}
static void test_detection() {
pcapdroid_t *pd = pd_init(PCAP_PATH "/metadata.pcap");
blacklist_t *bl = blacklist_init();
assert(bl != NULL);
blacklist_t *wl = blacklist_init();
assert(wl != NULL);
pd->malware_detection.enabled = true;
pd->malware_detection.bl = bl;
pd->malware_detection.whitelist = wl;
// Load blacklist
blacklist_add_ipstr(bl, "1.1.1.1");
blacklist_add_ipstr(bl, "216.58.208.164");
blacklist_add_ipstr(bl, "2c9b:a9b9:83dd:d9d1::2003");
blacklist_add_ipstr(bl, "149.202.95.241");
blacklist_add_domain(bl, "google.it");
blacklist_add_domain(bl, "www.internetbadguys.com");
blacklist_add_domain(bl, "example.org");
// Load whitelist
blacklist_add_ipstr(wl, "149.202.95.241");
blacklist_add_domain(wl, "example.org");
// Run
pd->cb.send_connections_dump = detection_cb;
pd_run(pd);
pd_free(pd);
}
/* ******************************************************* */
int main(int argc, char **argv) {
add_test("match", test_match);
run_test(argc, argv);
add_test("detection", test_detection);
run_test(argc, argv);
return 0;
}

View File

@ -0,0 +1,70 @@
/*
* This file is part of PCAPdroid.
*
* PCAPdroid is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PCAPdroid is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PCAPdroid. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2022 - Emanuele Faranda
*/
#include "test_utils.h"
/* ******************************************************* */
// Called on send_connections_dump. pd->new_conns contains the dumped
// connections. Ensures that metadata is correctly extracted from
// network traffic.
static void extract_metadata_cb(pcapdroid_t *pd) {
conn_and_tuple_t *conn;
// DNS request without reply
conn = assert_conn(pd, IPPROTO_UDP, "8.8.8.8", 53, "example.org");
assert(conn->tuple.src_port == htons(48037));
// DNS (TCP)
assert_conn(pd, IPPROTO_TCP, "8.8.8.8", 53, "f-droid.org");
// Guess host name from previous DNS request
assert_conn(pd, IPPROTO_TCP, "149.202.95.241", 80, "f-droid.org");
// HTTP
conn = assert_conn(pd, IPPROTO_TCP, "216.58.208.164", 80, "www.google.com");
assert(!strcmp(conn->data->url, "www.google.com/imghp?test=1&v2=2"));
conn = assert_conn(pd, IPPROTO_TCP, "385d:1ee:e3c9:9c5f::2004", 80, "www.google.com");
assert(!strcmp(conn->data->url, "www.google.com/imghp?test=1&v2=2"));
// TLS
conn = assert_conn(pd, IPPROTO_TCP, "142.250.180.131", 443, "google.it");
assert(conn->data->l7proto == NDPI_PROTOCOL_TLS);
conn = assert_conn(pd, IPPROTO_TCP, "2ed5:9050:81e9:4b68:248:1893:25c8:1946", 443, "example.org");
assert(conn->data->l7proto == NDPI_PROTOCOL_TLS);
}
static void test_metadata_extraction() {
conn_and_tuple_t *conn;
pcapdroid_t *pd = pd_init(PCAP_PATH "/metadata.pcap");
pd->cb.send_connections_dump = extract_metadata_cb;
pd_run(pd);
pd_free(pd);
}
/* ******************************************************* */
int main(int argc, char **argv) {
add_test("extract", test_metadata_extraction);
run_test(argc, argv);
return 0;
}

View File

@ -0,0 +1,27 @@
## metadata.pcap
Contains HTTP, TLS and DNS connections for both IPv4 and IPv6, suitable to test DPI.
Connections:
```
[UDP4] 192.168.1.10:48037 -> 8.8.8.8:53 [example.org]
[UDP4] 192.168.1.10:38793 -> 8.8.8.8:53 [www.google.com]
[TCP4] 192.168.1.10:36922 -> 216.58.208.164:80 [www.google.com]
[UDP4] 192.168.1.10:48772 -> 8.8.8.8:53 [www.google.com]
[TCP6] 2001:db8:1234::1:49936 -> 385d:1ee:e3c9:9c5f::2004:80 [www.google.com]
[UDP4] 192.168.1.10:51080 -> 8.8.8.8:53 [google.it]
[TCP6] 2001:db8:1234::1:44904 -> 2c9b:a9b9:83dd:d9d1::2003:443 []
[TCP4] 192.168.1.10:51588 -> 142.250.180.131:443 [google.it]
[UDP4] 192.168.1.10:42218 -> 8.8.8.8:53 [www.google.it]
[TCP6] 2001:db8:1234::1:59424 -> 3a5d:15fe:e3cb:9c5f::2003:443 [www.google.it]
[ICMP4] 192.168.1.10:4 -> 1.1.1.1:0 []
[UDP4] 192.168.1.10:47987 -> 8.8.8.8:53 [www.internetbadguys.com]
[TCP4] 192.168.1.10:46312 -> 146.112.255.155:80 [www.internetbadguys.com]
[UDP4] 192.168.1.10:51165 -> 8.8.8.8:53 [www.internetbadguys.com]
[UDP4] 192.168.1.10:52176 -> 8.8.8.8:53 [example.org]
[TCP6] 2001:db8:1234::1:45226 -> 2ed5:9050:81e9:4b68:248:1893:25c8:1946:443 [example.org]
[TCP4] 192.168.1.10:43453 -> 8.8.8.8:53 [f-droid.org]
[UDP4] 192.168.1.10:41011 -> 8.8.8.8:53 [f-droid.org]
[TCP4] 192.168.1.10:52782 -> 149.202.95.241:80 [f-droid.org]
```

Binary file not shown.

View File

@ -98,7 +98,7 @@ int main(int argc, char *argv[]) {
log_i("Capturing packets from %s", ifname);
pd_run(pd);
log_i("Terminated");
log_i("Cleanup...");
pd_free(pd);
free(ifname);
}

View File

@ -88,3 +88,36 @@ pcapdroid_t* pd_init(const char *ifname) {
return pd;
}
/* ******************************************************* */
/* To be called during send_connections_dump. Looks up a connection
* matching the specified protocol, IP port and info (if not NULL).
* If no connection is found, abort is called. Only the first match is
* returned.
*/
conn_and_tuple_t* assert_conn(pcapdroid_t *pd, int ipproto, const char *dst_ip,
uint16_t dst_port, const char *info) {
conn_and_tuple_t *found = NULL;
zdtun_ip_t ip;
dst_port = htons(dst_port);
int ipver = zdtun_parse_ip(dst_ip, &ip);
assert((ipver == 4) || (ipver == 6));
for(int i=0; i < pd->new_conns.cur_items; i++) {
conn_and_tuple_t *conn = &pd->new_conns.items[i];
if((conn->tuple.ipproto == ipproto) &&
(conn->tuple.dst_port == dst_port) &&
(conn->tuple.ipver == ipver) &&
(!zdtun_cmp_ip(ipver, &conn->tuple.dst_ip, &ip)) &&
((info == NULL) || ((conn->data->info != NULL) && !strcmp(info, conn->data->info)))) {
found = conn;
break;
}
}
assert(found);
return found;
}

View File

@ -26,13 +26,14 @@
#define assert0(x) assert((x) == 0)
#define assert1(x) assert((x) == 1)
#define PCAP_PATH "../pcap"
void add_test(const char *name, void (*test_cb)());
void run_test(int argc, char **argv);
pcapdroid_t* pd_init(const char *ifname);
static inline void pd_free(pcapdroid_t *pd) { free(pd); }
static inline void pd_free(pcapdroid_t *pd) {
free(pd);
}
conn_and_tuple_t* assert_conn(pcapdroid_t *pd, int ipproto, const char *dst_ip, uint16_t dst_port, const char *info);
#endif

23
docs/testing.md Normal file
View File

@ -0,0 +1,23 @@
Tests in PCAPdroid can be split in the following categories:
- [Java tests](https://github.com/emanuele-f/PCAPdroid/tree/dev/app/src/test/java):
they can be run via `./gradlew test`. They use the
[robolectric framework](https://github.com/robolectric/robolectric)
to mock the Android API, allowing them to be run locally (without an emulator)
- [Native tests](https://github.com/emanuele-f/PCAPdroid/tree/dev/app/src/main/jni/tests):
they can be run with `./run_tests.sh` on a linux host. They are built
with the [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html)
to detect memory issues and leaks
The tests are executed on every push via the
[Github workflows](https://github.com/emanuele-f/PCAPdroid/tree/dev/.github/workflows)
Apart from automatic tests, the following manual tests should be performed
before every release:
- Test on devices matching the `minSdkVersion` (currently Android SDK 21)
- Test on devices matching the `targetSdkVersion` (currently Android SDK 31)
- Rotate the device, put activity in background, clear from recent activities
- Java memory consumption tests via the [Memory Profiler](https://developer.android.com/studio/profile/memory-profiler)
- Manual malware detection test against `internetbadguys.com` and `0.0.0.1`

@ -1 +1 @@
Subproject commit e536c593d1fe49c67617911b18bda23a13138e5d
Subproject commit 240ae6459c9ef55c90ab8fd4fea1e60d8b5ad8b2