本文导航
〇、写在前面
笔者一直想尝试编写Android平台的抓包APP,于是网上调研了些资料,发现大部分文章都说移植libpcap+jpcap或者libpcap+jnetpcap的方式来实现,但按照他们所说的编译方式step by step执行,发现基本编译都通不过,很是蛋疼。后来经过一番折腾通过自己的方式完成了Android平台下的编译和使用。
一、编译libpcap和jpcap
1. 下载源代码
1.1 libpcap
git clone https://android.googlesource.com/platform/external/libpcap
1.2 jpcap
jpcap这个有点坑,我先尝试下载了官方仓库源代码,发现编译文件有makefile和build.xml,说明它支持gnu make和ant build两种方式。都尝试了一下,不出意外都失败了。
后来在git上找到了某个jpcap仓库,基于它编译竟然OK,读者请下载这个仓库的代码。
2. 编译libpcap
libpcap网上都说使用ndk-build去编译,自己创建Android.mk去编译,我嫌麻烦就直接使用NDK_TOOLS_CHAINS编译,编译前请配置环境变量,请读者参照自己的实际目录更改,如下所示:
export ANDROID_NDK=${android_ndk_root} ;\
export CC=${ANDROID_NDK}/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc ;\
export CCOPT="-O2 -fpic --sysroot=/your-path-to-android-ndk-toolchain-sysroot -DANDROID -DOS_ANDROID" ;\
export CFLAGS="--sysroot=/your-path-to-android-ndk-toolchain-sysroot -DANDROID -DOS_ANDROID -I./usr/include " ;\
export LDFLAGS="--sysroot=/your-path-to-android-ndk-toolchain-sysroot -fPIC -mandroid -L./usr/lib" ;\
./configure --host=arm-linux --with-pcap=linux --prefix=/your-path-to-android-ndk-toolchain-sysroot/usr
然后一键make:
make
最后生成libpcap.a, libpcap.so.1.9.0。
3. 编译jpcap
前面说了使用gnu make和ant build去编译都失败了,于是乎去网上各种搜看看别人是怎么编译jpcap的,后来找到了在 Cygwin 下编译Android的libpcap.a和libjpcap.so这篇文章,然后参照他的方法编译。
重新创建任意名称目录“/a/b/jni”,然后把libpcap里面的“libpcap.a、libpcap.so、pcap.c、pcap.h、pcap-bpf.c、pcap-bpf.h”拷贝到jni下面,还需要某些头文件也要搞过来。
cp /path-to-your-libpcap/libpcap.a .
cp /path-to-your-libpcap/pcap.c .
cp /path-to-your-libpcap/pcap.h .
cp /path-to-your-libpcap/pcap-bpf.c .
cp /path-to-your-libpcap/pcap-bpf.h .
cp -r /path-to-your-libpcap/pcap .
然后再把jpcap里面所有的cpp和c文件拷贝过来,以及部分头文件(Jpcap_sub.h、Jpcap_ether.h、jpcap_JpcapCaptor.h、jpcap_JpcapSender.h、jpcap_JpcapSender.h)也需要拷贝过来。
for i in `find -E /path-to-your-jpcap -regex '.*\.(c|cpp)'` ; do cp $i .;done
接着,创建Android.mk文件,内容如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
#LOCAL_MODULE:= libpcap
#LOCAL_SRC_FILES:= $(LOCAL_PATH)/libs/libpcap.a
#include $(PREBUILT_STATIC_LIBRARY)
#include $(CLEAR_VARS)
LOCAL_MODULE:= jpcap
LOCAL_SRC_FILES:=\
JpcapCaptor.c\
JpcapSender.c\
JpcapWriter.c\
packet_arp.c\
packet_datalink.c\
packet_icmp.c\
packet_ip.c\
packet_ipv6.c\
packet_tcp.c\
packet_udp.c
LOCAL_CFLAGS:=-O2 -g
LOCAL_CFLAGS+=-DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -Dlinux -D__GLIBC__ -D_GNU_SOURCE
LOCAL_LDFLAGS := $(LOCAL_PATH)/libs/libpcap.so
include $(BUILD_SHARED_LIBRARY)
这个文件内容也改了好几次,先前是引用静态库libpcap.a编译,结果报错undefine的符号引用没找到的问题。于是干脆换成了引用动态库的方式,从注释部分也看得出来。
最后,我一键ndk-build,结果还报错说是第三方库是“不兼容的目标”,排查后知道原因是ndk-build默认使用了32位编译,然我们的libpcap.so是64位的,于是新建Application.mk文件,添加编译64位目标程序的配置:
APP_ABI := arm64-v8a
这次终于一键ndk-build成功了。生成了libjpcap.so文件。
二、Android APP使用jpcap
1. 导入jpcap.jar和jpcap.so到Android项目
Android studio导入第三方jar包比较容易,不需要特定的存放路径规定,我先想着把它放在了app/src/main/Libs下面,同时也把jpcap.so放在了该目录下面,同时配置app/build.gradle内容,并在dependencies section下面添加如下引用:
dependencies {
... ...
compile files('src/main/Libs/jpcap.jar')
}
然后创建jpcap的引用模块代码,网上搜到的jpcap usage事例代码加到SnifferTraffic.java里面,主要是获取网络设备列表。
然后编译生成apk放到手机上运行,结果生成的apk运行后报错,提示libjpcap.so未找到,adb shell到app安装目录下面查看lib文件夹中确实没有任何库文件,说明jpcap.so并没有嵌到apk包里面去。经过一番搜索在一篇博客中找到了答案,原来是jpcap.so存放的路径有问题,最简单的解决办法是在src/main/下面创建jniLibs目录,然后将所有native so放在这下面,更新下dependencies section中引用:
dependencies {
... ...
compile files('src/main/jniLibs/jpcap.jar')
}
OK,解决了路径存放的问题,现在编译并运行,结果还是报错,提示libpcap.so.1找不到,这里我陷入了一个误区,误将libpcap.so.1看成了libjpcap.so.1,以为是jpcap还是没正确导入。然后各种改文件名称或者创建链接文件,比如将libjpcap.so改名位libjpcap.so.1试图解决,结果始终提示libpcap.so.1找不到。说明解决方法有问题。
后来仔细一看,卧槽!提示的名字是libpcap.so.1,并不是libjpcap.so.1啊。说明jpcap导入正确了,但是libpcap没有导入正确。然后IDA看来下jpcap.so引用的第三方库就包含libjpcap.so.1,问题终于找到了。那么直接把上面编译成功的libpcap.so重命名成libpcap.so.1,放到jniLibs目录下面呗。
OK,解决了libjpcap.so.1的问题,再次编译并运行,结果仍然报错,还是提示libjpcap.so.1找不到,adb shell到app安装目录下面查看lib文件夹中发现只有libjpcap.so一个文件,并没有libpcap.so.1文件,经过一番折腾得知后缀xx.so.1这种文件根本就不会导入。
然后继续网上搜答案,找到了这篇文章,这篇文章的意思就是说把so文件名称、so ELF文件结构中的SONAME名称、以及java代码里面的System.loadLibrary(soname)这三者要保持一致。仔细分析并没有什么帮助,又走了弯路。
anyway,自己分析解决吧,不就是导入的第三方库名称是xx.so.1,然后xx.so.1又没有办法放入apk里面嘛。我们强行改写导入的第三方库名称为xx.so呗,然后放入xx.so不就行了嘛。好,我们来试一下。首先010editor搜索jpcap.so里面的字符串xx.so.1,然后更改为xx.so保存,然后把xx.so也放到jniLibs下面。
最后,再次编译运行,OK了,不再报错xx库找不到的问题了。代码run起来了。哈哈哈~
但是还有问题,设备列表获取为空,网上说是APP不具备root权限问题。暂时还没解这个问题。后续更新吧。