编译Android版本的OpenVPN

0x00 前言

目前网上看到的Android端OpenVPN客户端都是基于VPNService开发的,这种方法不需要root权限,因此使用非常广泛。但是,这种方法需要UI操作,还是不够完美,我更喜欢使用纯命令行的openvpn(需要root权限)。

以下是编译2.3.17 x86版本openvpn过程的记录。

编译环境为:Ubuntu 14.04 64位系统

源码下载地址为:http://build.openvpn.net/downloads/releases/

0x01 依赖库编译

openvpn依赖的库主要有:openssl、liblzo

编译openssl

github上找到一个提供编译方法的项目,它提供的是openssl-1.1.0f版本的编译,按照如下命令可以很快编译出x86版本的静态库。

./build-openssl4android.sh android-x86 x86

但是后来发现,openvpn用到的一些函数已经不支持openssl 1.1以上版本,因此改用1.0.2版本的openssl。下载地址为:https://www.openssl.org/source/old/1.0.2/

修改build-openssl4android.sh中openssl的文件名,然后运行脚本编译。完成后在会在libs\x86\lib目录下生成libcrypto.alibssl.a两个文件。

网上还找到另外一种方法,原理是一致的,方法略有差异。

export CC="$STANDALONE_TOOCHAIN_PATH/bin/i686-linux-android-gcc -mtune=atom -march=atom --sysroot=$STANDALONE_TOOCHAIN_PATH/sysroot"
export AR=$STANDALONE_TOOCHAIN_PATH/bin/i686-linux-android-ar
export RANLIB=$STANDALONE_TOOCHAIN_PATH/bin/i686-linux-android-ranlib
./Configure android-x86 -DOPENSSL_IA32_SSE2 -DAES_ASM -DVPAES_ASM
make

$STANDALONE_TOOCHAIN_PATH是生成的工具链目录,使用build-openssl4android.sh会自动生成该目录。

经过测试,也是可以的。

编译liblzo

liblzo源码可以从http://www.oberhumer.com/opensource/lzo/download/下载。

编译可以参考这个项目,在源码根目录下创建jni目录并进入该目录,创建Android.mk文件,输入以下内容:

LOCAL_PATH:= $(call my-dir)

common_SRC_FILES:=  \
        ../src/lzo_crc.c \
        ../src/lzo_init.c \
        ../src/lzo_ptr.c \
        ../src/lzo_str.c \
        ../src/lzo_util.c \
        ../src/lzo1.c \
        ../src/lzo1_99.c \
        ../src/lzo1a.c \
        ../src/lzo1a_99.c \
        ../src/lzo1b_1.c \
        ../src/lzo1b_2.c \
        ../src/lzo1b_3.c \
        ../src/lzo1b_4.c \
        ../src/lzo1b_5.c \
        ../src/lzo1b_6.c \
        ../src/lzo1b_7.c \
        ../src/lzo1b_8.c \
        ../src/lzo1b_9.c \
        ../src/lzo1b_99.c \
        ../src/lzo1b_9x.c \
        ../src/lzo1b_cc.c \
        ../src/lzo1b_d1.c \
        ../src/lzo1b_d2.c \
        ../src/lzo1b_rr.c \
        ../src/lzo1b_xx.c \
        ../src/lzo1c_1.c \
        ../src/lzo1c_2.c \
        ../src/lzo1c_3.c \
        ../src/lzo1c_4.c \
        ../src/lzo1c_5.c \
        ../src/lzo1c_6.c \
        ../src/lzo1c_7.c \
        ../src/lzo1c_8.c \
        ../src/lzo1c_9.c \
        ../src/lzo1c_99.c \
        ../src/lzo1c_d1.c \
        ../src/lzo1c_d2.c \
        ../src/lzo1c_rr.c \
        ../src/lzo1c_xx.c \
        ../src/lzo1f_1.c \
        ../src/lzo1f_9x.c \
        ../src/lzo1f_d1.c \
        ../src/lzo1f_d2.c \
        ../src/lzo1x_1.c \
        ../src/lzo1x_9x.c \
        ../src/lzo1x_d1.c \
        ../src/lzo1x_d2.c \
        ../src/lzo1x_d3.c \
        ../src/lzo1x_o.c \
        ../src/lzo1x_1k.c \
        ../src/lzo1x_1l.c \
        ../src/lzo1x_1o.c \
        ../src/lzo1y_1.c \
        ../src/lzo1y_9x.c \
        ../src/lzo1y_d1.c \
        ../src/lzo1y_d2.c \
        ../src/lzo1y_d3.c \
        ../src/lzo1y_o.c \
        ../src/lzo1z_9x.c \
        ../src/lzo1z_d1.c \
        ../src/lzo1z_d2.c \
        ../src/lzo1z_d3.c \
        ../src/lzo2a_9x.c \
        ../src/lzo2a_d1.c \
        ../src/lzo2a_d2.c


common_C_INCLUDES += $(LOCAL_PATH)/../include

# static library
# =====================================================

include $(CLEAR_VARS)
LOCAL_SRC_FILES:= $(common_SRC_FILES)
LOCAL_C_INCLUDES:= $(common_C_INCLUDES)
LOCAL_MODULE := liblzo
LOCAL_PRELINK_MODULE:= false
include $(BUILD_STATIC_LIBRARY)

然后,再创建Application.mk文件,输入以下内容:

# The ARMv7 is significanly faster due to the use of the hardware FPU
NDK_TOOLCHAIN_VERSION := 4.9
APP_ABI := x86  
#
APP_PLATFORM := android-8
#APP_STL:=stlport_static
APP_STL := gnustl_static
APP_CPPFLAGS += -std=c++11

这样可以只编译x86版本。

最后,调用ndk编译($NDK为NDK根目录)。

$NDK/ndk-build

编译完会生成/path/to/liblzo/source/obj/local/x86/liblzo.a文件。

0x02 编译openvpn

环境准备

进入openvpn源码根目录,执行:

./configure

这步操作主要是为了生成config.h文件,里面包含了编译时用到的各种宏定义。

在源码根目录下创建jni目录,将config.h拷贝到jni目录中,并编辑该文件。

  • 注释掉#define HAVE_GETPASS 1行,因为Android中没有getpass函数
  • #define IFCONFIG_PATH "/sbin/ifconfig"修改为#define IFCONFIG_PATH "/system/bin/ifconfig"
  • #define IPROUTE_PATH "/sbin/ip"修改为#define IPROUTE_PATH "/system/bin/ip"
  • #define ROUTE_PATH "/sbin/route"修改为#define ROUTE_PATH "/system/bin/route"

进入jni目录,使用前面的方法创建Application.mk文件;然后创建Android.mk文件,输入以下内容(修改自:https://github.com/fries/android-external-openvpn/blob/master/Android.mk):

LOCAL_PATH:= $(call my-dir)

#on a 32bit maschine run ./configure --enable-password-save --disable-pkcs11 --with-ifconfig-path=/system/bin/ifconfig --with-route-path=/system/bin/route
#from generated Makefile copy variable contents of openvpn_SOURCES to common_SRC_FILES
# append missing.c to the end of the list
# missing.c defines undefined functions.
# in tun.c replace /dev/net/tun with /dev/tun


include $(CLEAR_VARS)
LOCAL_MODULE := libcrypto
LOCAL_SRC_FILES := $(LOCAL_PATH)/openssl/$(TARGET_ARCH_ABI)/lib/libcrypto.a
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := libssl
LOCAL_SRC_FILES := $(LOCAL_PATH)/openssl/$(TARGET_ARCH_ABI)/lib/libssl.a
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := liblzo
LOCAL_SRC_FILES := $(LOCAL_PATH)/liblzo/$(TARGET_ARCH_ABI)/lib/liblzo.a
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)

common_SRC_FILES:= \
        ../src/openvpn/openvpn.c \
        ../src/openvpn/base64.c \
        ../src/openvpn/buffer.c \
        ../src/openvpn/clinat.c \
        ../src/openvpn/console.c \
        ../src/openvpn/cryptoapi.c \
        ../src/openvpn/crypto.c \
        ../src/openvpn/crypto_openssl.c \
        ../src/openvpn/dhcp.c \
        ../src/openvpn/error.c \
        ../src/openvpn/event.c \
        ../src/openvpn/fdmisc.c \
        ../src/openvpn/forward.c \
        ../src/openvpn/fragment.c \
        ../src/openvpn/gremlin.c \
        ../src/openvpn/helper.c \
        ../src/openvpn/httpdigest.c \
        ../src/openvpn/init.c \
        ../src/openvpn/interval.c \
        ../src/openvpn/list.c \
        ../src/openvpn/lladdr.c \
        ../src/openvpn/lzo.c \
        ../src/openvpn/manage.c \
        ../src/openvpn/mbuf.c \
        ../src/openvpn/misc.c \
        ../src/openvpn/mroute.c \
        ../src/openvpn/mss.c \
        ../src/openvpn/mstats.c \
        ../src/openvpn/mtcp.c \
        ../src/openvpn/mtu.c \
        ../src/openvpn/mudp.c \
        ../src/openvpn/multi.c \
        ../src/openvpn/ntlm.c \
        ../src/openvpn/occ.c \
        ../src/openvpn/options.c \
        ../src/openvpn/otime.c \
        ../src/openvpn/packet_id.c \
        ../src/openvpn/perf.c \
        ../src/openvpn/pf.c \
        ../src/openvpn/ping.c \
        ../src/openvpn/pkcs11.c \
        ../src/openvpn/pkcs11_openssl.c \
        ../src/openvpn/platform.c \
        ../src/openvpn/plugin.c \
        ../src/openvpn/pool.c \
        ../src/openvpn/proto.c \
        ../src/openvpn/proxy.c \
        ../src/openvpn/ps.c \
        ../src/openvpn/push.c \
        ../src/openvpn/reliable.c \
        ../src/openvpn/route.c \
        ../src/openvpn/schedule.c \
        ../src/openvpn/session_id.c \
        ../src/openvpn/shaper.c \
        ../src/openvpn/sig.c \
        ../src/openvpn/socket.c \
        ../src/openvpn/socks.c \
        ../src/openvpn/ssl.c \
        ../src/openvpn/ssl_openssl.c \
        ../src/openvpn/ssl_verify.c \
        ../src/openvpn/ssl_verify_openssl.c \
        ../src/openvpn/status.c \
        ../src/openvpn/tun.c 

common_CFLAGS += -DHAVE_CONFIG_H

common_C_INCLUDES += \
        $(LOCAL_PATH)/openssl/include \
        $(LOCAL_PATH)/liblzo/include \
        $(LOCAL_PATH)/../include \
        $(LOCAL_PATH)/../src/compat

common_SHARED_LIBRARIES := 



# static linked binary
# =====================================================

include $(CLEAR_VARS)
LOCAL_SRC_FILES:= $(common_SRC_FILES)
LOCAL_CFLAGS:= $(common_CFLAGS)
LOCAL_C_INCLUDES:= $(common_C_INCLUDES)

LOCAL_SHARED_LIBRARIES += $(common_SHARED_LIBRARIES)
LOCAL_STATIC_LIBRARIES:= libssl libcrypto liblzo

LOCAL_MODULE:= openvpn
LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
include $(BUILD_EXECUTABLE)

libcrypto.alibssl.a拷贝到jni/openssl/x86/lib目录下(自行创建)。

liblzo.a拷贝到jni/liblzo/x86/lib目录下。

将liblzo源码根目录下的include目录拷贝到jni/liblzo目录下。

将openssl源码根目录下的include目录拷贝到jni/openssl目录下。

开始编译

$NDK/ndk-build

完成后,会生成/path/to/openvpn/source/libs/x86/openvpn文件。

0x03 使用Android模拟器测试

openvpn拷贝到模拟器,修改可执行权限;准备好ovpn文件。

  1. 确保ovpn文件中存在dev-node /dev/tun这行,因为Android中和Linux中tun设备路径不一致
  2. 由于默认临时目录为/tmp,需要修改为export TMPDIR=/data/local/tmp;不要通过在命令行增加--tmp-dir /data/local/tmp的方法来实现,否则会导致ovpn文件里的配置项无法被正常加载
  3. ip rule add from 0/0 table main pref 1000修改默认路由表的优先级,避免openvpn修改的路由信息不生效(我不确定这种做法会不会有副作用)
  4. ./openvpn client.ovpn

如果一切顺利的话,此时已经连接成功,可以使用浏览器打开ip138.com进行测试。

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Linux驱动

第3阶段——内核启动分析之prepare_namespace()如何挂载根文件系统和mtd分区介绍(6)

内核启动并初始化后,最终目的是像Windows一样能启动应用程序,在windows中每个应用程序都存在C盘、D盘等,而linux中每个应用程序是存放在根文件系统...

2968
来自专栏开发与安全

linux网络编程之POSIX 共享内存和 系列函数

在前面介绍了system v 共享内存的相关知识,现在来稍微看看posix 共享内存 和系列函数。 共享内存简单来说就是一块真正的物理内存区域,可以使用一些函数...

1920
来自专栏深度学习之tensorflow实战篇

pycharm 或者其他Python IDE不支持中文编码的解决方案

Python的自带编辑器IDLE或者Python Shell在默认情况下都不支持中文编码,若在脚本程序中出现中文,则会出现一定的错误。 原因: 因为Python...

2704
来自专栏Youngxj

emlog让微语支持html代码

2034

如何用split命令来拆分文件

split是一个类似于grep或tail的Unix命令行实用程序。它允许您将较大的文件分成几个较小的文件。

643
来自专栏我是业余自学C/C++的

C++中的文件和流

1064
来自专栏程序员互动联盟

【专业技术】如何在Linux中添加新的系统调用

系统调用是应用程序和操作系统内核之间的功能接口。其主要目的是使得用户可以使用操作系统提供的有关设备管理、输入/输入系统、文件系统和进程控制、通信以及存储管理等方...

2384
来自专栏Linux驱动

Linux-grep 命令和find 命令 (6)

grep与find区别: grep:查找指定目录下过滤文本文件中行数据 find:查找指定目录下文件  grep使用 格式:   grep "text"   *...

1729
来自专栏java学习

面试题49(关于import语句掌握)

有一个源代码,只包含importjava.util.* ; 这一个import语句,下面叙述正确的是? ---- A 只能写在源代码的第一句 B 可以访问ja...

3115
来自专栏IT笔记

如何限制input输入类型

1.只能输入和粘贴汉字 <input onkeyup="value=value.replace(/1/g,'')" onbeforepaste="clipbo...

3247

扫码关注云+社区