纯纯的白嫖!

前言

本文仅供学习探讨之用,如果侵犯了您的权益请联系我删除。

资源

  1. Github Pages
  2. Termux app and packages for legacy Android OS
  3. Apktool

介绍

Github Pages的基本使用就不介绍了,如果还不知道的话建议先阅读一下官方文档

这里只说几个需要的关注点

  1. 创建站点前得先创建一个站点仓库,即以xxx.github.io为名字的仓库,具体可看这里
  2. 每个站点的仓库大小限制为1GB(官方推荐),但是实测最大限制为1.49GB,不超过这个限制没啥问题,超过的话会上传失败。
  3. 如果你其他的仓库启用了Github Pages,则它对应的访问路径为xxx.github.io/仓库名,区分大小写。

搭建

假设你已经有了站点仓库(以xxx.github.io为名的仓库,没有的话请看介绍里的内容如何创建),在Github中新建一个用于此项目的空仓库,我这里起名为TermuxLegacy。

然后在本地新建一个项目文件夹,使用git init命令初始化空仓库。

使用git remote add origin 仓库地址添加TermuxLegacy仓库的地址,例如我这里是https://github.com/Bzi-Han/TermuxLegacy.git

官方历史仓库中下载termux-repositories-legacy-24.12.2019.tar文件,把它解压到项目目录下。

添加.gitignore文件,用于屏蔽过大的文件,防止仓库总大小超过了Github仓库的限制。

然后编写一个py脚本来扫描目录下的大文件,获取文件名单,毕竟手动添加那也太噩梦了。

import os

maxFileSize = 3 * 1024 * 1024 # 3MB
filterFileNames = set()
remainingFileSize = 0

for root, dirs, files in os.walk('termux-repositories-legacy/webroot/termux-packages/dists/stable/main'):
    for filename in files:
        size = os.path.getsize(os.path.join(root, filename))
        if maxFileSize <= size:
            filterFileNames.add(filename)
        else:
            remainingFileSize += size

for filename in filterFileNames:
    print(filename)

print(remainingFileSize, "{:.2f}MB".format(remainingFileSize / 1024. / 1024.), "{:.2f}GB".format(remainingFileSize / 1024. / 1024. / 1024.))

maxFileSize就是限制的单个文件最大大小。

运行一下

...
syncthing_1.3.0-1_x86_64.deb
nodejs-lts_12.13.0-2_i686.deb
git_2.23.0-1_i686.deb
quickjs-static_2019.10.27-1_aarch64.deb
1146961401 1093.83MB 1.07GB

可以看到排除掉大于3MB的文件之后只剩下1.07GB的文件了,符合我们的基本要求。

把扫描到的文件名直接复制粘贴到.gitignore中,然后再添加unstable-packages-21x11-packages-21这两个目录的屏蔽,只留下game、science、root和主仓库这4个仓库。

.gitignore文件

# repository
unstable-packages-21
x11-packages-21

# package
...

接下来只需要将仓库提交并上传即可

  1. 执行git add .将目录下所有的变动添加到本地仓库缓存中。
  2. 执行git commit -m "First commit"将缓存中的所有变动提交到本地仓库中。
  3. 执行git push origin master将本地仓库推送到Github仓库里。

推送完成之后在仓库的Settings->Pages中把Branch的分支设置成你仓库的主分支,路径设置为/,然后保存即可开启Github Pages。

如下图所示

Github Pages开启

开启之后需要等待Github Pages的编译部署,等待提交记录上的棕色圈圈变成绿色勾勾就行了

Github Pages编译

可以尝试访问一下,我这里的地址是https://bzi-han.github.io/TermuxLegacy,如果能看到README.md里的内容说明部署成功。

然后就是按照sources.list的格式进行配置访问路径即可,例如主仓库就是deb https://bzi-han.github.io/TermuxLegacy/termux-repositories-legacy/webroot/termux-packages stable main

如果你不了解的话,这是sources.list的格式文档

简单来说就是deb [选项] 软件包源地址 发布版名称 其他描述信息或者deb-src [选项] 软件包源地址 发布版名称 其他描述信息,对应二进制包和源代码包。

为了部分情况更方便的使用,我们还可以编写一个一键使用本仓库的bash脚本,上代码

#!/bin/bash
baseUrl="https://bzi-han.github.io/TermuxLegacy"

mainRepository="deb $baseUrl/termux-repositories-legacy/webroot/termux-packages stable main"
gameRepository="deb $baseUrl/termux-repositories-legacy/webroot/game-packages-21 games stable"
scienceRepository="deb $baseUrl/termux-repositories-legacy/webroot/science-packages-21 science stable"

rootRepository="deb $baseUrl/termux-repositories-legacy/webroot/termux-root-packages-21 root stable"

# x11Repository=""
# unstableRepository=""

echo "[=] Checking permission..."
if [ -z ${EUID+x} ]; then
    EUID=$(id -u)
fi
if [ ! -z ${PREFIX+x} ]; then
    if [[ $EUID -eq 0 ]]; then
        echo "[-] Do not run this script as the root user."
        exit
    fi
fi
echo "[=] User" $(whoami)

echo "[=] Checking environment..."
if [ -z ${PREFIX+x} ]; then
    PREFIX="/data/data/com.termux/files/usr"
fi

echo "[*] Installing repositories..."
# main repository
if [ -f "$PREFIX/etc/apt/sources.list" ]; then
    mv $PREFIX/etc/apt/sources.list $PREFIX/etc/apt/sources.list.bak
fi
echo $mainRepository > $PREFIX/etc/apt/sources.list.d/termux-legacy-main.list

# game repository
if [ -f "$PREFIX/etc/apt/sources.list.d/game.list" ]; then
    mv $PREFIX/etc/apt/sources.list.d/game.list $PREFIX/etc/apt/sources.list.d/game.list.bak
fi
echo $gameRepository > $PREFIX/etc/apt/sources.list.d/termux-legacy-game.list

# science repository
if [ -f "$PREFIX/etc/apt/sources.list.d/science.list" ]; then
    mv $PREFIX/etc/apt/sources.list.d/science.list $PREFIX/etc/apt/sources.list.d/science.list.bak
fi
echo $scienceRepository > $PREFIX/etc/apt/sources.list.d/termux-legacy-science.list

# root repository
if [ -f "$PREFIX/etc/apt/sources.list.d/root.list" ]; then
    mv $PREFIX/etc/apt/sources.list.d/root.list $PREFIX/etc/apt/sources.list.d/root.list.bak
fi
echo $rootRepository > $PREFIX/etc/apt/sources.list.d/termux-legacy-root.list

if [[ $EUID -eq 0 ]]; then
    echo "[*] Changing files ownership..."
    chown 10143:10143 $PREFIX/etc/apt/sources.list.d/termux-legacy-main.list
    chown 10143:10143 $PREFIX/etc/apt/sources.list.d/termux-legacy-game.list
    chown 10143:10143 $PREFIX/etc/apt/sources.list.d/termux-legacy-science.list
    chown 10143:10143 $PREFIX/etc/apt/sources.list.d/termux-legacy-root.list
    echo "[+] Change succeeded."
fi
echo "[+] Install successfully."

到这里,Termux仓库就已经算是搭建好了。

测试

如何使用可以看这里

直接来看效果

测试结果01 测试结果02 测试结果03

可以看到结果符合我们的预期。

修改App制作直接安装版

为了使得使用上更方便,我们直接修改App的默认配置,使其安装完后便直接可以使用Github Pages搭建的仓库。

虽然原本就是开源项目,但我不想直接编译整个App,太慢也比较烦。

Termux是将默认的虚拟环境打包在libtermux-bootstrap.so中的。

在首次运行App的时候会调用JNI函数Java_com_termux_app_TermuxInstaller_getZip来获取虚拟环境压缩包,并解压到/data/data/com.termux/files/usr进行配置。

于是这里我们可以直接修改虚拟环境压缩包中的环境配置,然后重新编译每一份适配各种平台的libtermux-bootstrap.so用来替换Apk中的库,并对Apk进行重新签名来实现。

搭建项目

我们先从原本的项目中拿出termux-bootstrap-zip.Stermux-bootstrap.c两个源文件,新建一个C++项目并把这两个源文件搞里头。

项目结构01

我这里用的是CMake+NDK来进行编译,你也可以直接使用NDK-Build,只是个人更喜欢CMake罢了。

编写CMakeLists.txt文件

cmake_minimum_required(VERSION 3.0.0)

enable_language(ASM)

project(termux-bootstrap)

set(CMAKE_CXX_STANDARD 20)

add_library(${PROJECT_NAME} SHARED ${CMAKE_SOURCE_DIR}/src/termux-bootstrap.c ${CMAKE_SOURCE_DIR}/src/termux-bootstrap-zip.S)
set_target_properties(${PROJECT_NAME} PROPERTIES 
    LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/lib/${ANDROID_ABI}"
)

编写build.bat批量编译arm64-v8a armeabi-v7a x86 x86_64这4个平台的脚本文件

@echo off

SETLOCAL ENABLEDELAYEDEXPANSION

SET "ARGV01=%~1"
SET "ARGV02=%~2"
SET "ARGV03=%~3"

@REM Initialize CMAKE_TOOLCHAIN_FILE
IF NOT DEFINED NDK_PATH (
    SET NDK_PATH=%ARGV01%
) ELSE IF DEFINED ARGV01 (
    SET NDK_PATH=%ARGV01%
)
IF NOT DEFINED NDK_PATH (
    SET NDK_PATH=E:/Android/Ndk/android-ndk-r25b
)
SET CMAKE_TOOLCHAIN_FILE=%NDK_PATH%/build/cmake/android.toolchain.cmake
IF NOT EXIST %CMAKE_TOOLCHAIN_FILE% (
    ECHO [-] CMAKE_TOOLCHAIN_FILE %CMAKE_TOOLCHAIN_FILE% does not exist
    EXIT
)

@REM Initialize ANDROID_PLATFORM
SET MINSDKVERSION=%ARGV02%
IF DEFINED MINSDKVERSION (
    SET ANDROID_PLATFORM=android-%MINSDKVERSION%
) ELSE (
    SET ANDROID_PLATFORM=android-23
)

@REM Initialize ANDROID_ABIS
SET ANDROID_ABIS=arm64-v8a armeabi-v7a x86 x86_64

@REM Check cmake tools
WHERE /Q cmake
SET IS_CMAKE_EXIST=%ERRORLEVEL%
SET CMAKE_TOOLS=cmake
IF DEFINED ARGV03 (
    SET CMAKE_TOOLS=%ARGV03%/bin/cmake.exe
) ELSE IF %IS_CMAKE_EXIST% == 1 (
    SET CMAKE_TOOLS=E:/Android/Sdk/cmake/3.22.1/bin/cmake.exe
)
IF NOT %CMAKE_TOOLS% == cmake (
    IF NOT EXIST %CMAKE_TOOLS%  (
        ECHO [-] CMAKE_TOOLS %CMAKE_TOOLS% does not exist
        EXIT
    )
)

@REM Echo environment
ECHO [=] CMAKE_TOOLCHAIN_FILE %CMAKE_TOOLCHAIN_FILE%
ECHO [=] ANDROID_PLATFORM %ANDROID_PLATFORM%
ECHO [=] ANDROID_ABIS %ANDROID_ABIS%
ECHO [=] CMAKE_TOOLS %CMAKE_TOOLS%

@REM Build
FOR %%A IN (%ANDROID_ABIS%) DO (
    ECHO [*] Configuration %%A...
    ECHO [=] Execute command %CMAKE_TOOLS% -DCMAKE_TOOLCHAIN_FILE=%CMAKE_TOOLCHAIN_FILE% -DANDROID_PLATFORM=%ANDROID_PLATFORM% -DANDROID_ABI=%%A -S . -B build -G Ninja
    %CMAKE_TOOLS% -DCMAKE_TOOLCHAIN_FILE=%CMAKE_TOOLCHAIN_FILE% -DANDROID_PLATFORM=%ANDROID_PLATFORM% -DANDROID_ABI=%%A -S . -B build -G Ninja
    ECHO [+] Done.

    ECHO [*] Build %%A...
    ECHO [=] Execute command %CMAKE_TOOLS% --build build --config Release
    %CMAKE_TOOLS% --build build --config Release
    ECHO [+] Done.
)

ECHO All libraries build successfully.

ENDLOCAL

官方历史仓库中下载bootstrap-archives-legacy-24.12.2019.tar压缩包,新建一个binary目录并将压缩包解压进去,如图所示

项目结构02

修改termux-bootstrap-zip.S文件的代码,把.incbin指令指向的文件路径修改为我们自己的路径

    .global blob
    .global blob_size
    .section .rodata
blob:
#if defined __i686__
    .incbin "../binary/bootstrap-archives/bootstrap-i686.zip"
#elif defined __x86_64__
    .incbin "../binary/bootstrap-archives/bootstrap-x86_64.zip"
#elif defined __aarch64__
    .incbin "../binary/bootstrap-archives/bootstrap-aarch64.zip"
#elif defined __arm__
    .incbin "../binary/bootstrap-archives/bootstrap-arm.zip"
#else
# error Unsupported arch
#endif
1:
blob_size:
    .int 1b - blob

修改虚拟环境

这里以aarch64为例,其他的操作流程都是一样的,不进行赘述。

我们将bootstrap-aarch64.zip进行解压,然后进入/etc/apt/目录中,把sources.list文件和/etc/apt/sources.list.d目录中的game.list science.list都做一个备份

项目结构03

然后分别对这三个文件的内容进行修改。

sources.list

# The main termux repository:
deb https://bzi-han.github.io/TermuxLegacy/termux-repositories-legacy/webroot/termux-packages stable main

game.list

deb https://bzi-han.github.io/TermuxLegacy/termux-repositories-legacy/webroot/game-packages-21 games stable

science.list

deb https://bzi-han.github.io/TermuxLegacy/termux-repositories-legacy/webroot/science-packages-21 science stable

然后再在/etc/apt/sources.list.d目录添加一个root.list

deb https://bzi-han.github.io/TermuxLegacy/termux-repositories-legacy/webroot/termux-root-packages-21 root stable

修改完成之后再把它打包成bootstrap-aarch64.zip即可。

编译Bootstrap

编译直接执行前面编写的build.bat文件即可,有3个可选参数分别是

  1. Android NDK路径
  2. 最低支持的Android SDK版本
  3. CMake路径

如果不指定的话则自动判断当前环境以及使用默认值。

运行脚本进行编译,输出

[=] CMAKE_TOOLCHAIN_FILE E:/Android/Ndk/android-ndk-r25b/build/cmake/android.toolchain.cmake
[=] ANDROID_PLATFORM android-23
[=] ANDROID_ABIS arm64-v8a armeabi-v7a x86 x86_64
[=] CMAKE_TOOLS E:/Android/Sdk/cmake/3.22.1/bin/cmake.exe
[*] Configuration arm64-v8a...
[=] Execute command E:/Android/Sdk/cmake/3.22.1/bin/cmake.exe -DCMAKE_TOOLCHAIN_FILE=E:/Android/Ndk/android-ndk-r25b/build/cmake/android.toolchain.cmake -DANDROID_PLATFORM=android-23 -DANDROID_ABI=arm64-v8a -S . -B build -G Ninja
-- The ASM compiler identification is Clang with GNU-like command-line
-- Found assembler: E:/Android/Ndk/android-ndk-r25b/toolchains/llvm/prebuilt/windows-x86_64/bin/clang.exe
-- The C compiler identification is Clang 14.0.6
-- The CXX compiler identification is Clang 14.0.6
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: E:/Android/Ndk/android-ndk-r25b/toolchains/llvm/prebuilt/windows-x86_64/bin/clang.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: E:/Android/Ndk/android-ndk-r25b/toolchains/llvm/prebuilt/windows-x86_64/bin/clang++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: D:/TermuxLegacyBootstrap/build
[+] Done.
[*] Build arm64-v8a...
[=] Execute command E:/Android/Sdk/cmake/3.22.1/bin/cmake.exe --build build --config Release
[3/3] Linking C shared library ..\lib\arm64-v8a\libtermux-bootstrap.so
[+] Done.
[*] Configuration armeabi-v7a...
[=] Execute command E:/Android/Sdk/cmake/3.22.1/bin/cmake.exe -DCMAKE_TOOLCHAIN_FILE=E:/Android/Ndk/android-ndk-r25b/build/cmake/android.toolchain.cmake -DANDROID_PLATFORM=android-23 -DANDROID_ABI=armeabi-v7a -S . -B build -G Ninja
-- Configuring done
-- Generating done
-- Build files have been written to: D:/TermuxLegacyBootstrap/build
[+] Done.
[*] Build armeabi-v7a...
[=] Execute command E:/Android/Sdk/cmake/3.22.1/bin/cmake.exe --build build --config Release
[3/3] Linking C shared library ..\lib\armeabi-v7a\libtermux-bootstrap.so
[+] Done.
[*] Configuration x86...
[=] Execute command E:/Android/Sdk/cmake/3.22.1/bin/cmake.exe -DCMAKE_TOOLCHAIN_FILE=E:/Android/Ndk/android-ndk-r25b/build/cmake/android.toolchain.cmake -DANDROID_PLATFORM=android-23 -DANDROID_ABI=x86 -S . -B build -G Ninja
-- Configuring done
-- Generating done
-- Build files have been written to: D:/TermuxLegacyBootstrap/build
[+] Done.
[*] Build x86...
[=] Execute command E:/Android/Sdk/cmake/3.22.1/bin/cmake.exe --build build --config Release
[3/3] Linking C shared library ..\lib\x86\libtermux-bootstrap.so
[+] Done.
[*] Configuration x86_64...
[=] Execute command E:/Android/Sdk/cmake/3.22.1/bin/cmake.exe -DCMAKE_TOOLCHAIN_FILE=E:/Android/Ndk/android-ndk-r25b/build/cmake/android.toolchain.cmake -DANDROID_PLATFORM=android-23 -DANDROID_ABI=x86_64 -S . -B build -G Ninja
-- Configuring done
-- Generating done
-- Build files have been written to: D:/TermuxLegacyBootstrap/build
[+] Done.
[*] Build x86_64...
[=] Execute command E:/Android/Sdk/cmake/3.22.1/bin/cmake.exe --build build --config Release
[3/3] Linking C shared library ..\lib\x86_64\libtermux-bootstrap.so
[+] Done.
All libraries build successfully.

编译完成后所有的库会生成在目录下的lib文件夹中

项目结构04

然后就可以进行下一步了。

替换与重打包Apk并进行重新签名

官方历史仓库中下载termux-v0.79-offline-bootstraps.apk文件,用Apktool给它解包出来出来

执行apktool d termux-v0.79-offline-bootstraps.apk

项目结构05

可以看到有个lib目录,这里面存的就是虚拟环境的那些东西,然后把我们刚才编译出来的lib文件夹直接复制过去并替换掉同样的文件。

完成之后再用apktool b termux-v0.79-offline-bootstraps命令重新打包为Apk即可。

接下来我们对Apk进行重签名,先使用工具生成一个我们自己的密钥,可以参考

这里不进行演示,生成好密钥之后我们需要在Android SDK目录里找到zipalign工具和apksigner.bat脚本,一般情况下路径为${SDK_PATH}\build-tools\${SDK_VERSION}\,例如我这里是E:\Android\Sdk\build-tools\33.0.0\

  1. 执行zipalign -v 4 termux-v0.79-offline-bootstraps.zip termux-v0.79-offline-bootstraps.apk使Apk的结构对齐。
  2. 执行apksigner.bat sign --ks Termux.keystore termux-v0.79-offline-bootstraps.apk对Apk进行重签名,其中Termux.keystore是上面使用工具生成的密钥。

签名完成之后termux-v0.79-offline-bootstraps.apk就已经是我们修改好之后的Apk了,在安装好Apk后首次初始化释放的虚拟环境就是对应我们前面修改的虚拟环境。

仓库

以上的所有东西都可以在这两个仓库中找到

结语

水水水。

那就这样了,有缘再见~