前期准备
本次使用的是基于 Android NDK 去交叉编译 Python ,使得在 Android 上也能玩 Python 。编译过程中相关工具必不可少,如下:
Python
:前往官网下载对应版本源码,链接如下:https://www.python.orgNDK
:交叉编译需要的工具,本文编译工具采用的是截止写文前最新的 NDK 25 ,下载链接如下(需科学网):https://developer.android.google.cn/ndk/downloads?hl=zh-cnOpenSSL
:加密库,链接如下:https://www.openssl.org/source/LibreSSL
:加密库(高版本 Python 编译使用 OpenSSL 库有时出现一些问题),该库同 OpenSSL ,相当于替代库,链接如下:https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/zlib
:编译需要的库,链接如下:https://www.zlib.net/gcc
:系统的gcc
版本需高于 8 ,若不满足,请更换系统或升级gcc
版本。- 以及中途可能遇到的库等,基本可使用
apt
命令安装。
本文基于 Debian 系 Linux 系统进行编译操作演示,且交叉编译 Python 的时候最好保证系统上的 Python 版本和要编译的 Python 版本一致,即先编译安装 Linux 版本的,再编译 arm 版本。
为模拟 Android 的文件系统路径,故此处会将 Python 编译至 /data/local/tmp
目录下,创建目录:
mkdir -p /data/local/tmp
此处我们将上述的工具等上传到 /data/local
目录下,此处我们编译一份 Python 3.7
的版本。先将相关压缩包解压,解压命令如下:
# 解压 NDK
unzip android-ndk-r25c-linux.zip
# 解压 OpenSSL
tar -zxvf openssl-1.1.1w.tar.gz
# 解压 zlib
tar -zxvf zlib-1.3.tar.gz
# 解压 Python3.7
tar -xvf Python-3.7.17.tar.xz
# 解压 LibreSSL
tar -zxvf libressl-3.8.1.tar.gz
最终如下图:
下面开始对相关组件等交叉编译,下述操作建议在 root
用户下操作。
交叉编译 zlib
编译该工具是让 Python 支持使用 pip 命令等工具。
zlib
的 configure
不支持设置 --host
项,因此我们先直接 configure
一下,然后手动更改生成的 Makefile
文件,相关命令如下:
# 前往 zlib 目录
root@ubuntu:/data/local# cd zlib-1.3/
# 创建 build 和 out 文件夹
root@ubuntu:/data/local/zlib-1.3# mkdir -p build out
# 前往 build 目录
root@ubuntu:/data/local/zlib-1.3# cd build/
# 执行 configure 命令
root@ubuntu:/data/local/zlib-1.3/build# ../configure --prefix=/data/local/zlib-1.3/out
Checking for gcc...
Checking for shared library support...
Building shared library libz.so.1.3 with gcc.
Checking for size_t... Yes.
Checking for off64_t... Yes.
Checking for fseeko... Yes.
Checking for strerror... Yes.
Checking for unistd.h... Yes.
Checking for stdarg.h... Yes.
Checking whether to use vs[n]printf() or s[n]printf()... using vs[n]printf().
Checking for vsnprintf() in stdio.h... Yes.
Checking for return value of vsnprintf()... Yes.
Checking for attribute(visibility) support... Yes.
root@ubuntu:/data/local/zlib-1.3/build#
使用 vim
命令打开编辑 Makefile
文件,将其中的 CC
、 AR
、 RANLIB
都修改为 arm-linux
交叉编译器的相关参数,也将 LDSHARED
和 CPP
两项中的 gcc
替换为 CC
修改后的内容,具体修改地方如下:
root@ubuntu:/data/local/zlib-1.3/build# vim Makefile
……
CC=/data/local/android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android33-clang
……
LDSHARED=/data/local/android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android33-clang -shared -Wl,-soname,libz.so.1,--version-script,../zlib.map
CPP=/data/local/android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android33-clang -E
……
AR=/data/local/android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar
……
RANLIB=/data/local/android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib
……
Makefile 文件修改后保存退出,接下来执行下面两条命令,完成交叉编译:
make
make install
编译后的内容在 zlib 源码的 out 目录下。
交叉编译 OpenSSL
编译该工具是使得 Python 支持加密协议。
前往 OpenSSL
目录,将下述脚本保存到 OpenSSL 源码的同目录下,脚本内容如下,部分内容参数请按提示自定义更新:
#!/bin/bash
COMPILE_ROOT="$(dirname $(readlink -f "$0"))"
# NDK 目录,按需更换
export ANDROID_NDK_ROOT=/data/local/android-ndk-r25c
export ANDROID_GCC_ROOT=${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64
export ANDROID_GCC_PATH=${ANDROID_GCC_ROOT}/bin
BUILD_PATH=${COMPILE_ROOT}/build
OUT_PATH=${COMPILE_ROOT}/out
CROSS_COMPILER=aarch64-linux-android-
CROSS_COMPILER_CLANG=aarch64-linux-android33-
#prepare
mkdir -p ${BUILD_PATH}
mkdir -p ${OUT_PATH}
export PATH=${ANDROID_NDK_ROOT}:${ANDROID_GCC_PATH}:$PATH
export ARCH="aarch64"
export CC="${CROSS_COMPILER_CLANG}clang -pie -fPIE"
export CPP="${CROSS_COMPILER_CLANG}clang -E -pie -fPIE"
export CXX="${CROSS_COMPILER_CLANG}clang++ -pie -fPIE"
# 下方的链接文件,新版 NDK 变成了 llvm-xx 的文件格式了,可能旧版的还存在诸如 aarch64-linux-android-xx 的格式文件,若为旧版格式文件,可将下面注释的取消注释,并将 llvm-xx 格式的命令参数反向注释掉,按需修改
#export AS="${CROSS_COMPILER}as"
export LD="${CROSS_COMPILER}ld -pie -fPIE"
export GDB="${CROSS_COMPILER}gdb"
export STRIP="${CROSS_COMPILER}strip"
#export RANLIB="${CROSS_COMPILER}ranlib"
#export OBJCOPY="${CROSS_COMPILER}objcopy"
#export OBJDUMP="${CROSS_COMPILER}objdump"
#export AR="${CROSS_COMPILER}ar"
#export NM="${CROSS_COMPILER}nm"
export RANLIB="llvm-ranlib"
export AR="llvm-ar"
export NM="llvm-nm"
export OBJCOPY="llvm-objcopy"
export OBJDUMP="llvm-objdump"
export AS="llvm-as"
#export READELF="${CROSS_COMPILER}readelf"
export READELF="llvm-readelf"
export M4=m4
export TARGET_PREFIX=$CROSS_COMPILER
export CXXFLAGS="-D__ANDROID_API__=33 "
cd ${BUILD_PATH}
config_soft(){
../config --prefix=${OUT_PATH} -no-asm -no-shared -no-async
}
make_soft(){
make -j $(nproc)
}
make_soft_install(){
make install
}
make_clean(){
make clean
}
case "$1" in
config_soft)
config_soft
;;
make_soft)
make_soft
;;
make_soft_install)
make_soft_install
;;
make_clean)
make_clean
;;
esac
先使用 config_soft
生成一些文件,命令如下:
root@ubuntu:/data/local/openssl-1.1.1w# vim make_openssl.sh
root@ubuntu:/data/local/openssl-1.1.1w# bash make_openssl.sh config_soft
Operating system: x86_64-whatever-linux2
Configuring OpenSSL version 1.1.1w (0x1010117fL) for linux-x86_64
Using os-specific seed configuration
Creating configdata.pm
Creating Makefile
**********************************************************************
*** ***
*** OpenSSL has been successfully configured ***
*** ***
*** If you encounter a problem while building, please open an ***
*** issue on GitHub <https://github.com/openssl/openssl/issues> ***
*** and include the output from the following command: ***
*** ***
*** perl configdata.pm --dump ***
*** ***
*** (If you are new to OpenSSL, you might want to consult the ***
*** 'Troubleshooting' section in the INSTALL file first) ***
*** ***
**********************************************************************
root@ubuntu:/data/local/openssl-1.1.1w#
然后在 make_soft
和 make_soft_install
完成 OpenSSL 的编译:
root@ubuntu:/data/local/openssl-1.1.1w# bash make_openssl.sh make_soft
……
root@ubuntu:/data/local/openssl-1.1.1w# bash make_openssl.sh make_soft_install
……
若无报错,即完成 OpenSSL 的交叉编译,编译后的内容在 OpenSSL 源码的 out 目录下。
交叉编译 LibreSSL
编译该工具是使得 Python 支持加密协议,相当于 OpenSSL 的替代。
前往 libressl
目录,将下述脚本保存到 libressl 源码的同目录下,脚本内容如下,部分内容参数请按提示自定义更新:
#!/bin/bash
COMPILE_ROOT="$(dirname $(readlink -f "$0"))"
# NDK 目录,按需更换
export ANDROID_NDK_ROOT=/data/local/android-ndk-r25c
export ANDROID_GCC_ROOT=${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64
export ANDROID_GCC_PATH=${ANDROID_GCC_ROOT}/bin
BUILD_PATH=${COMPILE_ROOT}/build
OUT_PATH=${COMPILE_ROOT}/out
CROSS_COMPILER=aarch64-linux-android-
CROSS_COMPILER_CLANG=aarch64-linux-android33-
#prepare
mkdir -p ${BUILD_PATH}
mkdir -p ${OUT_PATH}
export PATH=${ANDROID_NDK_ROOT}:${ANDROID_GCC_PATH}:$PATH
export ARCH="aarch64"
export CC="${CROSS_COMPILER_CLANG}clang -pie -fPIE"
export CPP="${CROSS_COMPILER_CLANG}clang -E -pie -fPIE"
export CXX="${CROSS_COMPILER_CLANG}clang++ -pie -fPIE"
# 下方的链接文件,新版 NDK 变成了 llvm-xx 的文件格式了,可能旧版的还存在诸如 aarch64-linux-android-xx 的格式文件,若为旧版格式文件,可将下面注释的取消注释,并将 llvm-xx 格式的命令参数反向注释掉,按需修改
#export AS="${CROSS_COMPILER}as"
export LD="${CROSS_COMPILER}ld -pie -fPIE"
export GDB="${CROSS_COMPILER}gdb"
export STRIP="${CROSS_COMPILER}strip"
#export RANLIB="${CROSS_COMPILER}ranlib"
#export OBJCOPY="${CROSS_COMPILER}objcopy"
#export OBJDUMP="${CROSS_COMPILER}objdump"
#export AR="${CROSS_COMPILER}ar"
#export NM="${CROSS_COMPILER}nm"
export RANLIB="llvm-ranlib"
export AR="llvm-ar"
export NM="llvm-nm"
export OBJCOPY="llvm-objcopy"
export OBJDUMP="llvm-objdump"
export AS="llvm-as"
#export READELF="${CROSS_COMPILER}readelf"
export READELF="llvm-readelf"
export M4=m4
export TARGET_PREFIX=$CROSS_COMPILER
export CXXFLAGS="-D__ANDROID_API__=33 "
cd ${BUILD_PATH}
config_soft(){
../configure --host=aarch64-linux-android \
--host=aarch64-linux \
--build=x86_64-linux-gnu \
--target=aarch64-linux-android \
--prefix=${OUT_PATH}
}
make_soft(){
make -j $(nproc)
}
make_soft_install(){
make install
}
make_clean(){
make clean
}
case "$1" in
config_soft)
config_soft
;;
make_soft)
make_soft
;;
make_soft_install)
make_soft_install
;;
make_clean)
make_clean
;;
esac
然后编译安装,安装的文件在 out
目录下:
root@ubuntu:/data/local/libressl-3.8.1# vim make_libressl.sh
root@ubuntu:/data/local/libressl-3.8.1# bash make_libressl.sh config_soft
……
root@ubuntu:/data/local/libressl-3.8.1# bash make_libressl.sh make_soft
……
root@ubuntu:/data/local/libressl-3.8.1# bash make_libressl.sh make_soft_install
……
交叉编译 Python
下面就到了交叉编译 Python 的环节了。
此处需注意一下,交叉编译前,系统内需要一份同版本的 Python ,所以我们先编译安装一份当前系统下的 Python ,命令如下:
# 先安装一些库
root@ubuntu:/data/local/Python-3.7.17# apt install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev git libgdbm-dev libdb-dev libpcap-dev libexpat1-dev
# 然后编译安装一份当前版本的 Python 。注意的是,此处不需要真正安装到系统中去,所以我将其安装在源码的 python3_bin 目录下,供下面的脚本需要
# 生成配置文件,--prefix按需修改,若修改,下面的脚本路径也需要你按需自定义
root@ubuntu:/data/local/Python-3.7.17# ./configure --prefix=/data/local/Python-3.7.17/python3_bin
# 编译安装
root@ubuntu:/data/local/Python-3.7.17# make && make install
# 安装操作结束后,清除一下 Python 源代码目录文件的一些编译过程文件
root@ubuntu:/data/local/Python-3.7.17# make clean
然后继续在 Python3.7
源码目录,将下述脚本保存到 Python3 源码的同目录下,脚本内容如下,部分内容参数请按提示自定义更新:
#!/bin/bash
# 此为上述编译后生成的 Python 3 二进制文件所在,并设置一下别名
export PATH=/data/local/Python-3.7.17/python3_bin/bin:$PATH
alias python='python3'
COMPILE_ROOT="$(dirname $(readlink -f "$0"))"
# NDK 路径
export ANDROID_NDK_ROOT=/data/local/android-ndk-r25c
export ANDROID_GCC_ROOT=${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64
export ANDROID_GCC_PATH=${ANDROID_GCC_ROOT}/bin
BUILD_PATH=${COMPILE_ROOT}/build
# 生成二进制文件的路径
OUT_PATH=/data/local/tmp/python3
# 若 OpenSSL 不能正常编译加密库,可换成 LibreSSL
OPENSSL_PATH=/data/local/openssl-1.1.1w/out
OPENSSL_LIB_PATH=/data/local/openssl-1.1.1w/out/lib
#OPENSSL_PATH=/data/local/libressl-3.8.1/out
#OPENSSL_LIB_PATH=/data/local/libressl-3.8.1/out/lib
CROSS_COMPILER=aarch64-linux-android-
CROSS_COMPILER_CLANG=aarch64-linux-android33-
#prepare
mkdir -p ${BUILD_PATH}
mkdir -p ${OUT_PATH}
export PATH=${ANDROID_NDK_ROOT}:${ANDROID_GCC_PATH}:$PATH
export ARCH="aarch64"
export CC="${CROSS_COMPILER_CLANG}clang -pie -fPIE"
export CPP="${CROSS_COMPILER_CLANG}clang -E -pie -fPIE"
export CXX="${CROSS_COMPILER_CLANG}clang++ -pie -fPIE"
# 下方的链接文件,新版 NDK 变成了 llvm-xx 的文件格式了,可能旧版的还存在诸如 aarch64-linux-android-xx 的格式文件,若为旧版格式文件,可将下面注释的取消注释,并将 llvm-xx 格式的命令参数反向注释掉,按需修改
#export AS="${CROSS_COMPILER}as"
export LD="${CROSS_COMPILER}ld -pie -fPIE"
export GDB="${CROSS_COMPILER}gdb"
export STRIP="${CROSS_COMPILER}strip"
#export RANLIB="${CROSS_COMPILER}ranlib"
#export OBJCOPY="${CROSS_COMPILER}objcopy"
#export OBJDUMP="${CROSS_COMPILER}objdump"
#export AR="${CROSS_COMPILER}ar"
#export NM="${CROSS_COMPILER}nm"
export RANLIB="llvm-ranlib"
export AR="llvm-ar"
export NM="llvm-nm"
export OBJCOPY="llvm-objcopy"
export OBJDUMP="llvm-objdump"
export AS="llvm-as"
#export READELF="${CROSS_COMPILER}readelf"
export READELF="llvm-readelf"
export M4=m4
export TARGET_PREFIX=$CROSS_COMPILER
export CONFIG_SITE="config.site"
export CXXFLAGS="-D__ANDROID_API__=33 "
cd ${BUILD_PATH}
config_soft(){
# 此处可在 Android 设备上查看一下是否存在 /dev/ptmx 和 /dev/ptc 设备文件,有就把参数设置成 yes ,无则设为 no 。
echo -e "ac_cv_file__dev_ptmx=yes\nac_cv_file__dev_ptc=no" > config.site
../configure --host=aarch64-linux-android \
--host=aarch64-linux \
--build=x86_64-pc-linux-gnu \
--target=aarch64-linux-android \
LDFLAGS="-Wl,--allow-shlib-undefined -D__ANDROID_API__=33 -fPIC -L${OPENSSL_LIB_PATH}" \
CFLAGS="-D__ANDROID_API__=33 " \
CPPFLAGS="-D__ANDROID_API__=33" \
--enable-ipv6 \
--with-openssl=${OPENSSL_PATH} \
--prefix=${OUT_PATH}
}
make_soft(){
make -j $(nproc)
}
make_soft_install(){
make install
}
make_clean(){
make clean
}
case "$1" in
config_soft)
config_soft
;;
make_soft)
make_soft
;;
make_soft_install)
make_soft_install
;;
make_clean)
make_clean
;;
esac
先使用 config_soft
生成一些文件,命令如下:
root@ubuntu:/data/local/Python-3.7.17# vim make_python.sh
root@ubuntu:/data/local/Python-3.7.17# bash make_python.sh config_soft
……
到这一步需要注意,如果其他平台编译时在 build/Modules
文件夹内生成了 Setup
文件,我们需要修改一下里面的部分参数内容,其中 SSL
参数是我们上面脚本里指定的哪个加密库,下方就改成对应的加密库。同时指定一下 zlib 库文件位置:
root@ubuntu:/data/local/Python-3.7.17# vim build/Modules/Setup
……
SSL=/data/local/openssl-1.1.1w/out
_ssl _ssl.c \
-DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \
-L$(SSL)/lib -lssl -lcrypto
……
zlib zlibmodule.c -I/data/local/zlib-1.3/out/include -L/data/local/zlib-1.3/out/lib -lz
……
然后再编译安装生成可执行二进制文件:
root@ubuntu:/data/local/Python-3.7.17# bash make_python.sh make_soft
……
root@ubuntu:/data/local/Python-3.7.17# bash make_python.sh make_soft_install
……
生成成功后接下来就是将 /data/local/tmp
目录下的文件压缩并使用 adb 推到 Android 设备的 /data/local/tmp
目录下:
# 打包 Python 3 文件
root@ubuntu:/data/local/tmp# tar -zcvf python3.tar.gz python3
# adb 将文件推送至手机
adb push python3.tar.gz /data/local/tmp
然后在 Android 系统下解压,即可使用该 Python ,如图:
接下来为更方便的调用,可写个脚本控制相关环境变量,脚本内容如下:
#!/system/bin/env sh
export HOME='/data/local/tmp/python3'
PYTHON_HOME='/data/local/tmp/python3'
export PATH=${PYTHON_HOME}/bin:${PATH}
export LD_LIBRARY_PATH=${PYTHON_HOME}/lib:${LD_LIBRARY_PATH}
export PYTHONPATH=${PYTHONPATH}:${PYTHON_HOME}
python3 "$@"
效果如下:
kali@shell:/data/local/tmp/python3 $ vi python_run.sh
kali@shell:/data/local/tmp/python3 $ chmod +x python_run.sh
kali@shell:/data/local/tmp/python3 $ ./python_run.sh --version
Python 3.7.17
kali@shell:/data/local/tmp/python3 $ ./python_run.sh
Python 3.7.17 (default, Sep 20 2023, 16:55:26)
[Clang 14.0.7 (https://android.googlesource.com/toolchain/llvm-project 4c603efb on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
至此,基于 NDK 交叉编译的 Python 3 已能够在 Android 上执行。自己在编译过程中发现 pip 模块缺失,可前往 Python 官网下载 get-pip.py
进行安装即可,如下图:
最后的最后,快淦饭了,就让 ChatGPT 给我生成一个标题吧:
个人自己编译的一份成品也将其打包分享给大家,若有兴趣的小伙伴可根据编译的成品 Python 二进制文件去制作成 Magisk 模块使得挂载到设备上使用:
提取链接:
https://pan.baidu.com/s/1Sxs7VxEVPQKSWI_AL0aszg?pwd=6666提取码:6666
3 条评论
我编译出了so文件,python3.12.1的版本,使用的是android api 26 在安卓8.0以上的手机上面都能正常运行,但是在8.0以下的手机上面,就加载失败,我把编译的android api 版本降到了21,编译出来的so文件还是没办法在8.0以下的手机上运行。大佬可以帮忙指导一下吗?
指导原因了 我编译的是3.11版本 需要增加 --with-build-python
您好 我参考您的博文编译Python-3.11.7,前面都很顺利 编译到python 时候 出现了错误 ,错误信息如下
checking build system type... x86_64-pc-linux-gnu
checking host system type... aarch64-unknown-linux-gnu
configure: error: Cross compiling requires --with-build-python
希望您能指导下 谢谢 。