seladb / PcapPlusPlus

PcapPlusPlus is a multiplatform C++ library for capturing, parsing and crafting of network packets. It is designed to be efficient, powerful and easy to use. It provides C++ wrappers for the most popular packet processing engines such as libpcap, Npcap, WinPcap, DPDK, AF_XDP and PF_RING.
https://pcapplusplus.github.io/
The Unlicense
2.68k stars 652 forks source link

Fail to link libpcap for android QT #1509

Closed ArkhamKn1ght closed 2 months ago

ArkhamKn1ght commented 2 months ago

I'm trying to link pcap++ with my empty qt qml qmake 6.5.3 project for android arm64-v8a Basically, the error is in libpcap:

:-1: error: error: undefined symbol: getifaddrs
fad-getad.c:-1: referenced by fad-getad.c
fad-getad.o:-1: fad-getad.o:(pcap_findalldevs_interfaces) in archive /home/arkhamkn1ght/dev/testinPcapPlusPlus/../../../../opt/libpcap/libpcap.a

:-1: error: error: undefined symbol: freeifaddrs
fad-getad.c:-1: referenced by fad-getad.c
fad-getad.o:-1: fad-getad.o:(pcap_findalldevs_interfaces) in archive /home/arkhamkn1ght/dev/testinPcapPlusPlus/../../../../opt/libpcap/libpcap.a

I downloaded prebuilt libcap for android here: https://github.com/seladb/libpcap-android . I used v30 Then I tried to build pcap++ myself. I succeeded and encountered the error above. Then I tried to use prebuilt pcap++ lib for android from here: https://github.com/seladb/PcapPlusPlus/releases/tag/v23.09 where I also encountered the same problem.

My android device is rooted, connected to PC via usb and debugging is on.

qmake file:

QT += quick
SOURCES += \
        main.cpp

resources.files = main.qml 
resources.prefix = /$${TARGET}
RESOURCES += resources
CONFIG += c++11
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =

# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

contains(ANDROID_TARGET_ARCH,arm64-v8a) {
    ANDROID_PACKAGE_SOURCE_DIR = \
        $$PWD/android
}

DISTFILES += \
    android/AndroidManifest.xml

unix:!macx: LIBS += -L$$PWD/../../../../opt/pcapplusplus/ -lCommon++

INCLUDEPATH += $$PWD/../../../../opt/pcapplusplus/include
DEPENDPATH += $$PWD/../../../../opt/pcapplusplus/include

unix:!macx: LIBS += -L$$PWD/../../../../opt/pcapplusplus/ -lPacket++

INCLUDEPATH += $$PWD/../../../../opt/pcapplusplus/include
DEPENDPATH += $$PWD/../../../../opt/pcapplusplus/include

unix:!macx: LIBS += -L$$PWD/../../../../opt/pcapplusplus/ -lPcap++

INCLUDEPATH += $$PWD/../../../../opt/pcapplusplus/include
DEPENDPATH += $$PWD/../../../../opt/pcapplusplus/include

unix:!macx: LIBS += -L$$PWD/../../../../opt/libpcap/ -lpcap

INCLUDEPATH += $$PWD/../../../../opt/libpcap/include
DEPENDPATH += $$PWD/../../../../opt/libpcap/include

main.cpp file:

#include <string>
#include <jni.h>
#include <android/log.h>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <IPv4Layer.h>
#include <Packet.h>
#include <PcapFileDevice.h>
#include <PacketUtils.h>
#include <DnsLayer.h>
#include <SSLLayer.h>
int main(int argc, char *argv[])
{
    pcpp::PcapFileReaderDevice reader("1_packet.pcap");
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/testinPcapPlusPlus/main.qml"));
    QObject::connect(
        &engine,
        &QQmlApplicationEngine::objectCreated,
        &app,
        [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        },
        Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

I would really appreciate the help.

UPD: I'm on ubuntu 22.04

seladb commented 2 months ago

@ArkhamKn1ght I don't know much about QT so I can't help with that. Did you try building your app using the instructions mentioned in PcapPlusPlus docs?

We run it regularly in our CI and it works.

I'm sorry I can't help with QT, but maybe you can take the process documented in our docs and "translate" it to QT?

ArkhamKn1ght commented 2 months ago

@seladb I managed to get it to working by recompiling libpcap, since the problem lied there. Basically, I "disabled" fad-getad.c in the libpcap's makefile.

I got a question: is it possible to use pcap++ for sniffing packets on android?

seladb commented 2 months ago

@seladb I managed to get it to working by recompiling libpcap, since the problem lied there. Basically, I "disabled" fad-getad.c in the libpcap's makefile.

Great! Thanks for letting us know!

I got a question: is it possible to use pcap++ for sniffing packets on android?

On rooted devices it should be possible, but I never actually tried it

ArkhamKn1ght commented 2 months ago

For people encountering this issue in the future: This https://gist.github.com/zhan6841/dd45e4d9bfcc60f67350b0f21a8a0369 was incredibly helpful, but needed few adjustments: in Android.mk file add missing .c files(linker will throw errors that it can't locate specific .c files) and then compile using NDK u installed.

ArkhamKn1ght commented 2 months ago

@seladb Now I have different problem. My device is rooted, but i fail to get liveDevice by name

QProcess serialProcess;
    serialProcess.start("su");
    serialProcess.waitForStarted(2000);
    // IPv4 address of the interface we want to sniff
    std::string interfaceIPAddr = "10.0.0.1";

    // find the interface by IP address
    pcpp::PcapLiveDevice* dev = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDeviceByName(u8"wlan0");
    if (dev == NULL)
    {
        qWarning() << "Cannot find interface with IPv4 address of '" << interfaceIPAddr;
        serialProcess.waitForFinished();
        return 1;
    }

It always goes into if scope. Any ideas ?

EDIT: it doesnt work even without u8

seladb commented 2 months ago

@ArkhamKn1ght do you see any devices when you do pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList()?

ArkhamKn1ght commented 2 months ago

@seladb nope. This:

for(int i = 0; i < pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList().size(); ++i) {
        std::cout << "1" << std::endl;
        std::cout << pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList()[i] << std::endl;
    }

outputs absolutely nothing

UPD: but getPcapLiveDeviceByName produces this info:

[ERROR: src/PcapLiveDeviceList.cpp: init:46          ] Error searching for devices: wlan0: SIOCETHTOOL(ETHTOOL_GLINK) ioctl failed: (null)
ArkhamKn1ght commented 2 months ago

PROBLEM SOLVED! Any other cursed soul that tries to compile pcap++ under QMAKE QT ANDROID, listen carefully:

  1. You need to compile libcap(not pcap++) almost manually. Look up for instructions. In case guthub page gets deleted, here is the copy:

For people encountering this issue in the future: This https://gist.github.com/zhan6841/dd45e4d9bfcc60f67350b0f21a8a0369 was incredibly helpful, but needed few adjustments: in Android.mk file add missing .c files(linker will throw errors that it can't locate specific .c files) and then compile using NDK u installed.

https://gist.github.com/zhan6841/dd45e4d9bfcc60f67350b0f21a8a0369 contents:

Intro
This is my personal note for crossing compile libpcap and tcpdump for Android. It inludes the issues I met as well as how I fixed them.

Hardware
Android Device: A rooted ASUS ROG Phone II
OS on the Linux Device: Ubuntu 18.04
Software
[Android tcpdump](https://www.androidtcpdump.com/android-tcpdump/compile)
[Android libpcap](https://android.googlesource.com/platform/external/libpcap/)
Android NDK
Cross Compiling Android tcpdump
You can simply following [this tutorial](https://www.androidtcpdump.com/android-tcpdump/compile), or just download their [complied file](https://www.androidtcpdump.com/android-tcpdump/downloads).

Cross Compiling Android libpcap
Clone Android libpcap source code from [this repo](https://android.googlesource.com/platform/external/libpcap/)
Since I do not use Soong, I manually converted the file libpcap/Android.bp file to an libpcap/Android.mk file. I built it as a static library.
I made following modifications:
First, in libpcap/Android.mk, I replace the file fad-getad.c with the file fad-gifc.c, otherwise there will be compiling errors as follows:
/xx/xx/TestAndroid/obj/local/arm64-v8a/libpcap.a(fad-getad.o): In function `pcap_findalldevs_interfaces':
/xx/xx/TestAndroid/jni/libpcap/fad-getad.c:169: undefined reference to `getifaddrs'
/xx/xx/TestAndroid/jni/libpcap/fad-getad.c:276: undefined reference to `freeifaddrs'
Second, I edited the file “config.h”, I commented the following code:
#define HAVE_DECL_ETHER_HOSTTON 1
#define HAVE_ETHER_HOSTTON 1
#define HAVE_LINUX_GETNETBYNAME_R 1
#define HAVE_LINUX_GETPROTOBYNAME_R 1
#define PCAP_SUPPORT_DBUS 1
#define PCAP_SUPPORT_RDMASNIFF
Otherwise, there will be compiling errors like these:
/xx/xx/TestAndroid/obj/local/arm64-v8a/libpcap.a(pcap.o): In function `pcap_findalldevs':
/xx/xx/TestAndroid/jni/libpcap/pcap.c:737: undefined reference to `dbus_findalldevs'
/xx/xx/TestAndroid/jni/libpcap/pcap.c:737: undefined reference to `rdmasniff_findalldevs'
/xx/xx/TestAndroid/obj/local/arm64-v8a/libpcap.a(pcap.o): In function `pcap_create':
/xx/xx/TestAndroid/jni/libpcap/pcap.c:2337: undefined reference to `dbus_create'
/xx/xx/TestAndroid/jni/libpcap/pcap.c:2337: undefined reference to `rdmasniff_create'
/xx/xx/TestAndroid/obj/local/arm64-v8a/libpcap.a(nametoaddr.o): In function `pcap_nametonetaddr':
/xx/xx/TestAndroid/jni/libpcap/nametoaddr.c:270: undefined reference to `getnetbyname_r'
/xx/xx/TestAndroid/obj/local/arm64-v8a/libpcap.a(nametoaddr.o): In function `pcap_nametoproto':
/xx/xx/TestAndroid/jni/libpcap/nametoaddr.c:527: undefined reference to `getprotobyname_r'
/xx/xx/TestAndroid/obj/local/arm64-v8a/libpcap.a(nametoaddr.o): In function `pcap_ether_hostton':
/xx/xx/TestAndroid/jni/libpcap/nametoaddr.c:790: undefined reference to `ether_hostton'
libpcap/Android.mk Script
LOCAL_PATH:=$(call my-dir)
include $(CLEAR_VARS)
TARGET_ARCH_ABI:=arm64-v8a
LOCAL_MODULE := pcap

# LOCAL_CFLAGS := -D_BSD_SOURCE
LOCAL_CFLAGS := -DHAVE_CONFIG_H
LOCAL_CFLAGS += -Dlint
LOCAL_CFLAGS += -D_U_="__attribute__((__unused__))"
LOCAL_CFLAGS += -Wall
# LOCAL_CFLAGS += -Werror
LOCAL_CFLAGS += -Wno-macro-redefined
LOCAL_CFLAGS += -Wno-pointer-arith
LOCAL_CFLAGS += -Wno-sign-compare
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_CFLAGS += -Wno-unused-result
LOCAL_CFLAGS += -Wno-tautological-compare

LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_SRC_FILES :=\
    pcap-linux.c\
    pcap-usb-linux.c\
    pcap-netfilter-linux-android.c\
    fad-gifc.c\
    pcap.c\
    gencode.c\
    optimize.c\
    nametoaddr.c\
    etherent.c\
    fmtutils.c\
    savefile.c\
    sf-pcap.c\
    sf-pcapng.c\
    pcap-common.c\
    bpf_image.c\
    bpf_filter.c\
    bpf_dump.c\
    scanner.c\
    grammar.c\
    missing/strlcpy.c
include $(BUILD_STATIC_LIBRARY)

# LOCAL_PATH:=$(call my-dir)
# include $(CLEAR_VARS)
# LOCAL_MODULE    := libpcap 
# LOCAL_SRC_FILES := libpcap.a
# include $(PREBUILT_STATIC_LIBRARY)
Useful References
[Android Official: Build your project](https://developer.android.com/ndk/guides/build)
[Android NDK: Link using a pre-compiled static library (Stack Overflow)](https://stackoverflow.com/questions/5463518/android-ndk-link-using-a-pre-compiled-static-library)
  1. Your android device must be rooted and SUPPORT monitor mode for wireless card inside the device
  2. YOU NEED TO SET DEVICE INTO PERMISSIVE MODE, OTHERWISE IT WONT EXPOSE INTERFACES TO YOU!

Good luck!

tigercosmos commented 2 months ago

@ArkhamKn1ght Thanks, it's good information.