看雪学院 10月09日 19:15
手动修改Frida源码以绕过检测
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文详细介绍了如何手动修改Frida的源代码,以绕过常见的检测机制。文章首先阐述了编译Frida源码的环境搭建过程,包括依赖安装、源码下载、Node.js和NDK版本的配置,并分享了编译过程中遇到的问题及解决方案,特别是针对12.8.0和14.2.18版本的编译尝试。随后,文章深入讲解了Frida的几种主要检测原理,并提供了相应的源码修改方法,包括混淆frida:rpc字符串、修改frida-server目录名、通信管道文件名、frida-agent.so文件名以及入口函数名等。通过这些修改,旨在帮助用户构建一个更隐蔽的Frida环境,以应对更复杂的安全检测。

🛠️ **编译环境搭建与版本适配**:文章详细列出了编译Frida源码所需的依赖库,并重点说明了根据不同Frida版本(如12.8.0和14.2.18)需要配置的Node.js和NDK版本。其中,对于12.8.0版本遇到的工具链缺失问题,作者通过分析源码和网络资源,指出了解决方案,并最终选择了14.2.18版本进行成功编译,强调了`--recurse-submodules`参数的重要性以及根据报错信息调整git协议和创建`master`分支的必要性。

🔍 **Frida RPC字符串混淆**:为了绕过基于`frida:rpc`字符串的检测,文章提供了一种使用Base62编码替换固定字符串的方法。通过在`session.vala`文件中添加Base62的字符集和解码函数,并将所有`frida:rpc`相关的字符串替换为Base62编码后的值,有效隐藏了Frida的通信标识,增加了检测难度。

📁 **目录和文件名混淆**:文章指出,Frida的默认目录名`re.frida.server`和`frida-agent-.so`文件名容易被应用扫描检测到。通过修改`server.vala`文件,将默认目录名改为随机生成的UUID,以及在`linux-host-session.vala`文件中修改AgentDescriptor的模板,使其生成随机前缀的`.so`文件名,从而避免了基于固定名称的特征检测。

📞 **通信管道和入口函数名混淆**:文章还提到了Frida用于进程注入的通信管道文件(如`linjector-xxxx`)和入口函数名等也可能成为检测点。通过修改`frida-helper-backend-glue.c`文件,将通信管道文件名与内存地址和ID关联,使其动态变化;同时,通过修改`frida-core/src/linux/linux-host-session.vala`中`AgentDescriptor`的构造函数,可以实现生成随机前缀的`.so`文件名,进一步增强了隐蔽性。

schimmer 2025-10-09 18:00 上海

看雪论坛作者ID:schimmer

环境:WSL Ubuntu 20.04.6 LTS

版本:frida14.2.18

查看网上很多很多手动魔改frida,绕过一些检测点教程都是直接通过git pach进去的,首先想自己手动修改一下源码,理清一下修改点和修改逻辑,所以直接在源码上修改添加内容的,其次我主用的frida12.8.0和frida14.2.18没有对应的patch,想要魔改该版本也只能依据其他版本的pach手动修改。文章内容比较长,步骤并非一次成功,包含了一些试错过程,复现时建议先全文读完,绕过试错过程,frida12.8.0由于工具链缺失没有完成编译,但步骤大同小异。

一、编译环境安装及测试

一开始选择的12.8.0版本,后面发现工具链下载不到没办法编译,后面换为14.2.18版本,流程基本是一样的。1.安装基础依赖

sudo apt install build-essential git python3-pip lib32stdc++-9-dev libc6-dev-i386 curl unzip

build-essential: 包含 C/C++ 编译器 (gcc/g++) 和make等基础开发工具。

git: 版本控制工具,用于从 GitHub 下载源码。

python3-pip: Python 的包管理器,用于安装后续的 Python 依赖。

lib32stdc++-9-dev&libc6-dev-i386: 32 位 C++ 标准库和 C 库的开发文件,交叉编译 32 位架构。(需要编译32位可以安装)

curl&unzip: 用于下载和解压文件。

2.下载源码

git clone --recurse-submodules -b 12.8.0 228K9s2c8https:
//github
.captom
/frida/frida

--recurse-submodules: 这是编译 Frida 关键的参数之一。作用是克隆主项目的同时,自动下载所有这些被依赖的“子项目”。如果没有这个参数,源码会不完整,编译时会因为缺少文件而失败。

-b 12.8.0:-b参数用来指定一个特定的版本。

使用git时注意用git config --global user.name "Your Name"git config --global user.email "youremail@yourdomain.com"配置一下用户名和邮箱

如果有网络问题使用git config --global http.proxy http://127.0.0.1:yourProxyPortgit config --global https.proxy http://127.0.0.1:yourProxyPort设置一下代理。

如果出现:

Cloning into 
'/home/schimmer/workspace/frida/frida12.8.0/frida/capstone'
...fatal: unable to connect to github.com:github.com[0: 20.205.243.166]: errno=Connection timed outfatal: clone of 
'git://github.com/frida/capstone.git'
 into submodule path 
'/home/schimmer/workspace/frida/frida12.8.0/frida/capstone'
 failedFailed to clone 
'capstone'
. Retry scheduledCloning into 
'/home/schimmer/workspace/frida/frida12.8.0/frida/frida-clr'
...fatal: unable to connect to github.com:github.com[0: 20.205.243.166]: errno=Connection timed out

换成https协议

git config --global url.
"https://github.com/"
.insteadOf git:
//github
.com/
# 继续下载cd fridagit submodule update --init --recursive

3. 查看并安装对应的Node.js和NDK版本查看版本信息

# 在package.json搜索node版本信息schimmer@jnga-20230821GW:~/workspace/frida/frida12.8.0/frida$ cat frida-node/package.json | grep node    "node": ">=8.0.0"    "@types/node": "^12.0.4",    "ts-node": "^8.1.0",    "install": "prebuild-install || node-gyp rebuild",    "test": "npm run prebuild && node --expose-gc node_modules/mocha/bin/_mocha -r ts-node/register test/*.ts"    "url": "https://github.com/frida/frida-node.git"    "url": "https://github.com/frida/frida-node/issues"    # 搜索ndk字段查看使用的ndk版本schimmer@jnga-20230821GW:~/workspace/frida/frida12.8.0/frida$ grep -i ndk Makefile.sdk.mk        ndk_abi := x86        ndk_triplet := i686-linux-android        ndk_abi := x86_64        ndk_triplet := x86_64-linux-android        ndk_abi := arm-linux-androideabi        ndk_triplet := arm-linux-androideabi        ndk_abi := aarch64-linux-android        ndk_triplet := aarch64-linux-android        ndk_build_platform_arch := $(shell uname -s | tr '[A-Z]' '[a-z]')-$(build_arch)        ndk_llvm_prefix := $(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/$(ndk_build_platform_arch)        ndk_gcc_prefix := $(ANDROID_NDK_ROOT)/toolchains/$(ndk_abi)-4.9/prebuilt/$(ndk_build_platform_arch)                CPP=clang CC=clang CXX=clang++ LD= LDFLAGS= AR=$(ndk_triplet)-ar RANLIB=$(ndk_triplet)-ranlib \                ANDROID_NDK=$(ANDROID_NDK_ROOT) \                PATH=$(ndk_llvm_prefix)/bin:$(ndk_gcc_prefix)/bin:$$PATH \                android_ndk_root="$(ANDROID_NDK_ROOT)" \                android_ndk_version="r20" \                android_ndk_major_version=20 \                android32_ndk_api_level=18 \                android64_ndk_api_level=21 \                clang_base_path="$(abspath $(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/$(ndk_build_platform_arch))"                                # 也可以在setup-env.sh文件看ndk版本schimmer@jnga-20230821GW:~/workspace/frida/frida12.8.0/frida$ cat releng/setup-env.sh | grep ndk  ndk_required_name="r20"      ndk_installed_version=$(grep Pkg.Revision "$ANDROID_NDK_ROOT/source.properties" | awk '{ split($NF, v, "."); print v[1]; }')      ndk_installed_version=$(cut -f1 -d" " "$ANDROID_NDK_ROOT/RELEASE.TXT")    if [ $ndk_installed_version -ne 20 ]; then        echo "Unsupported NDK version $ndk_installed_version. Please install NDK $ndk_required_name."        echo "Frida's SDK - the prebuilt dependencies snapshot - was compiled against $ndk_required_name,"        echo "releng/setup-env.sh and adjust the ndk_required variable. Make sure you use"    echo "ANDROID_NDK_ROOT must be set to the location of your $ndk_required_name NDK." > /dev/stderr

这里通过字段"node": ">=8.0.0""@types/node": "^12.0.4"android_ndk_version="r20" \android_ndk_major_version=20 \ndk_required_name="r20",可以确定node版本为12,ndk版本为r20,具体的小版本可能不一定找得到,尽量大版本对应。

安装nodejs 12推荐nvm安装,方法设置环境变量切换版本

# 安装nvmcurl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bashsource ~/.bashrc# 安装 node12nvm install 12# 使用12版本nvm use 12# 查看版本node -vv12.22.12

安装ndk r20具体小版本号要去官网看看有哪些

# 创建文件夹,解压的ndk文件到时候放这里,位置自行选择mkdir -p ~/Android/Sdk/ndk# 下载wget https://dl.google.com/android/repository/android-ndk-r20b-linux-x86_64.zip# 解压unzip android-ndk-r20b-linux-x86_64.zip -d ~/Android/Sdk/ndk# 设置当前shell环境变量,如果要长期生效需要自己写进~/.bashrc中export ANDROID_NDK_ROOT="$HOME/Android/Sdk/ndk/android-ndk-r20b"export PATH=$ANDROID_NDK_ROOT:$PATH

4. 编译测试直接make,会列出支持的所有编译选项

schimmer@jnga-20230821GW:~/workspace/frida/frida12.8.0/frida$ makemake[1]: Entering directory '/home/schimmer/workspace/frida/frida12.8.0/frida'Usage: make TARGET [VARIABLE=value]Where TARGET specifies one or more of:  /* gum */  gum-linux-x86                     Build for Linux/x86  gum-linux-x86_64                  Build for Linux/x86-64  gum-linux-x86-thin                Build for Linux/x86 without cross-arch support  gum-linux-x86_64-thin             Build for Linux/x86-64 without cross-arch support  gum-linux-arm                     Build for Linux/ARM  gum-linux-armbe8                  Build for Linux/ARMBE8  gum-linux-armhf                   Build for Linux/ARMhf  gum-linux-arm64                   Build for Linux/ARM64  gum-linux-mips                    Build for Linux/MIPS  gum-linux-mipsel                  Build for Linux/MIPSel  gum-linux-mips64                  Build for Linux/MIPS64  gum-linux-mips64el                Build for Linux/MIP64Sel  gum-android-x86                   Build for Android/x86  gum-android-x86_64                Build for Android/x86-64  gum-android-arm                   Build for Android/ARM  gum-android-arm64                 Build for Android/ARM64  gum-qnx-arm                       Build for QNX/ARM  gum-qnx-armeabi                   Build for QNX/ARMEABI  check-gum-linux-x86               Run tests for Linux/x86  check-gum-linux-x86_64            Run tests for Linux/x86-64  check-gum-linux-x86-thin          Run tests for Linux/x86 without cross-arch support  check-gum-linux-x86_64-thin       Run tests for Linux/x86-64 without cross-arch support  check-gum-linux-arm64             Run tests for Linux/ARM64  /* core */  core-linux-x86                    Build for Linux/x86  core-linux-x86_64                 Build for Linux/x86-64  core-linux-x86-thin               Build for Linux/x86 without cross-arch support  core-linux-x86_64-thin            Build for Linux/x86-64 without cross-arch support  core-linux-arm                    Build for Linux/ARM  core-linux-armbe8                 Build for Linux/ARMBE8  core-linux-armhf                  Build for Linux/ARMhf  core-linux-arm64                  Build for Linux/ARM64  core-linux-mips                   Build for Linux/MIPS  core-linux-mipsel                 Build for Linux/MIPSel  core-linux-mips64                 Build for Linux/MIPS64  core-linux-mips64el               Build for Linux/MIPS64el  core-android-x86                  Build for Android/x86  core-android-x86_64               Build for Android/x86-64  core-android-arm                  Build for Android/ARM  core-android-arm64                Build for Android/ARM64  core-qnx-arm                      Build for QNX/ARM  core-qnx-armeabi                  Build for QNX/ARMEABI  check-core-linux-x86              Run tests for Linux/x86  check-core-linux-x86_64           Run tests for Linux/x86-64  check-core-linux-x86-thin         Run tests for Linux/x86 without cross-arch support  check-core-linux-x86_64-thin      Run tests for Linux/x86-64 without cross-arch support  check-core-linux-arm64            Run tests for Linux/ARM64  /* python */  python-linux-x86                  Build Python bindings for Linux/x86  python-linux-x86_64               Build Python bindings for Linux/x86-64  python-linux-x86-thin             Build Python bindings for Linux/x86 without cross-arch support  python-linux-x86_64-thin          Build Python bindings for Linux/x86-64 without cross-arch support  python-linux-arm64                Build Python bindings for Linux/ARM64  check-python-linux-x86            Test Python bindings for Linux/x86  check-python-linux-x86_64         Test Python bindings for Linux/x86-64  check-python-linux-x86-thin       Test Python bindings for Linux/x86 without cross-arch support  check-python-linux-x86_64-thin    Test Python bindings for Linux/x86-64 without cross-arch support  check-python-linux-arm64          Test Python bindings for Linux/ARM64  /* node */  node-linux-x86                    Build Node.js bindings for Linux/x86  node-linux-x86_64                 Build Node.js bindings for Linux/x86-64  node-linux-x86-thin               Build Node.js bindings for Linux/x86 without cross-arch support  node-linux-x86_64-thin            Build Node.js bindings for Linux/x86-64 without cross-arch support  node-linux-arm64                  Build Node.js bindings for Linux/ARM64  check-node-linux-x86              Test Node.js bindings for Linux/x86  check-node-linux-x86_64           Test Node.js bindings for Linux/x86-64  check-node-linux-x86-thin         Test Node.js bindings for Linux/x86 without cross-arch support  check-node-linux-x86_64-thin      Test Node.js bindings for Linux/x86-64 without cross-arch support  check-node-linux-arm64            Test Node.js bindings for Linux/ARM64  /* tools */  tools-linux-x86                   Build CLI tools for Linux/x86  tools-linux-x86_64                Build CLI tools for Linux/x86-64  tools-linux-x86-thin              Build CLI tools for Linux/x86 without cross-arch support  tools-linux-x86_64-thin           Build CLI tools for Linux/x86-64 without cross-arch support  tools-linux-arm64                 Build CLI tools for Linux/ARM64  check-tools-linux-x86             Test CLI tools for Linux/x86  check-tools-linux-x86_64          Test CLI tools for Linux/x86-64  check-tools-linux-x86-thin        Test CLI tools for Linux/x86 without cross-arch support  check-tools-linux-x86_64-thin     Test CLI tools for Linux/x86-64 without cross-arch support  check-tools-linux-arm64           Test CLI tools for Linux/ARM64

这里我只需要支持64位Android的,选择core-android-arm64

schimmer@jnga-20230821GW:~/workspace/frida/frida12.8.0/frida$ make core-android-arm64
make[1]: Entering directory '/home/schimmer/workspace/frida/frida12.8.0/frida'
make[1]: *** No rule to make target '.git/refs/heads/master', needed by 'build/frida-version.h'.  Stop.
make[1]: Leaving directory '/home/schimmer/workspace/frida/frida12.8.0/frida'
make: *** [Makefile:4: core-android-arm64] Error 2

因为git的时候直接git clone -b 12.8.0,没有在master分支上所以报错,直接创建命名为master

schimmer@DESKT-SCHIMMER:~/workspace/frida/frida12.8.0/fridagit checkout -b master
Switched to a new branch 'master'
schimmer@DESKT-SCHIMMER:~/workspace/frida/frida12.8.0/fridamake core-android-arm64
make[1]: Entering directory '/home/schimmer/workspace/frida/frida12.8.0/frida'
FRIDA_HOST=linux-x86_64 \
        FRIDA_OPTIMIZATION_FLAGS="-Os" \
        FRIDA_DEBUG_FLAGS="-g3" \
        FRIDA_ASAN=no \
        ./releng/setup-env.sh
Downloading and deploying toolchain...
bzip2: (stdin) is not a bzip2 file.
tar: Child returned status 2
tar: Error is not recoverable: exiting now
make[1]: *** [releng/common.mk:17: build/frida-env-linux-x86_64.rc] Error 1
make[1]: Leaving directory '/home/schimmer/workspace/frida/frida12.8.0/frida'
make: *** [Makefile:4: core-android-arm64] Error 2

工具链已经不存在了。

其他教程说工具链需要手动下载,url上区别是不同版本的时间不同,也就是url中数字那一串不同

https://build.frida.re/deps/20210123/toolchain-linux-x86_64.tar.bz2

https://build.frida.re/deps/20210123/sdk-linux-x86_64.tar.bz2

https://build.frida.re/deps/20210123/sdk-android-arm.tar.bz2

https://build.frida.re/deps/20210123/sdk-android-arm64.tar.bz2

# 20210123是frida中releng/deps.mk中的frida_deps_version,后面部分则是toolchain-{平台+架构}.tar.bz2

看了一下12.8.0没有deps.mk

schimmer@jnga-20230821GW:~/workspace/frida/frida12.8.0/fridagrep -r "build.frida.re" .

./releng/setup-env.sh:    $download_command "https://build.frida.re/toolchain-${toolchain_version}-${build_platform}-${build_arch}.tar.bz2" tar -C "$FRIDA_TOOLROOT" -xj $tar_stdin || exit 1

./releng/setup-env.sh:    $download_command "https://build.frida.re/sdk-${sdk_version}-${host_platform}-${host_arch}.tar.bz2" tar -C "$FRIDA_SDKROOT" -xj $tar_stdin 2> /dev/null

./releng/windows-toolchain.txt:https://build.frida.re/toolchain-20190428-windows-x86.exe

./releng/build-deps-windows.py:BOOTSTRAP_TOOLCHAIN_URL = "https://build.frida.re/toolchain-20190428-windows-x86.exe"

./releng/release.py:                "s3cmd sync --delete-removed --acl-public pool/ s3://build.frida.re/pool/",

./releng/release.py:                "s3cmd put --acl-public Release Packages Packages.gz s3://build.frida.re/",

./releng/release.py:                "s3cmd put --acl-public Packages Packages.gz s3://build.frida.re/./",

./releng/release.py:        subprocess.call(["cfcli""purge"] + ["https://build.frida.re" + resource for resource in [

./releng/windows-sdk.txt:https://build.frida.re/sdk-20191024-windows-any.exe

schimmer@jnga-20230821GW:~/workspace/frida/frida12.8.0/fridagrep "toolchain_version=" releng/setup-env.sh

toolchain_version=20190428

schimmer@jnga-20230821GW:~/workspace/frida/frida12.8.0/fridagrep "sdk_version=" releng/setup-env.sh

sdk_version=20191024

  sdk_version="$sdk_version-asan"

直接字符串搜索了一下在setup-env.sh中,时间号为20191024和20190428,确实工具链地址已经不存在了,所以下载不到会报错,直接进去把这两个下载注释掉,直接不用工具链完全重新编译试试。

# 编译
make -j$(nproc) core-android-arm64

报错了

依赖库太复杂了不好编译,换个新点的有工具链的版本试试。

5. 编译测试14.2.18然后和上面一样下载安装配置好对应版本node和ndk环境变量进行make即可

编译能够正常下载工具链(网上有的帖子说要手动先下载工具链,但是我发现make的时候会自动下载,那就不用手动下载了)

能看到编译出来的文件,证明编译环境没问题,接下来push进手机测试一下(编译文件在frida/build/frida-android-arm64/bin

能正常使用,接下来修改frida特征重新编译

二、修改frida特征

1.混淆frida:rpc字符串检测原理:Frida 的客户端和注入到 App 里的服务端(frida-agent.so)之间通过交换 JSON 格式的消息来通信。为了识别这些消息是属于 Frida 的远程调用,每一条 RPC 相关的消息中都会包含一个固定的标识符frida:rpc,app可以对代码段进行扫描检测。

首先找到frida:rpc的位置,在`frida-core/lib/interfaces/session.vala`中。

网上很多公开的修改都是基于base64编码来规避的,这里我采用base62,避免公开的修改也被检测到。

先添加base62字符集和解码函数

// 自定义乱序字符集
const string BASE62_ALPHABET = "aAbBcC4dDeEfF1gGhHiIjJ8kKlLmM5nNoOpPqQrRsS2tTuUvV6wWxXyY9zZ703";
    // Base62 解码函数
public string base62_decode(string input) {
    // 存储解码后的字节
    var bytes = new ArrayList<uint8>();
    // 初始化:添加第一个字节(值为0)
    bytes.add(0);
    // 遍历输入字符串的每个字符
    foreach (uint8 c in input.to_utf8()) {
        int digit = BASE62_ALPHABET.index_of_char((char)c);
        // 初始化进位为0
        int carry = 0;
        // 处理所有现有字节(从最低位到最高位)
        for (int i = 0; i < bytes.size; i++) {
            // 当前字节值乘以62加上当前数字
            int value = bytes[i] * 62 + digit + carry;
            // 计算新的字节值和进位
            bytes[i] = (uint8)(value & 0xFF); // 取低8位
            carry = value >> 8;               // 进位(高8位)
            // 重置digit,只对第一个字节使用实际digit值
            digit = 0;
        }
        // 处理剩余的进位
        while (carry > 0) {
            bytes.add((uint8)(carry & 0xFF));
            carry >>= 8;
        }
    }
    // 反转字节顺序
    var reversed_bytes = new ArrayList<uint8>();
    for (int i = bytes.size - 1; i >= 0; i--) {
        reversed_bytes.add(bytes[i]);
    }
    
    reversed_bytes.add (0);//添加终止符
    // 将字节数组转换为 UTF-8 字符串
    return (string) reversed_bytes.to_array();
}

修改所有使用字符串位置

//  .add_string_value ("frida:rpc")
.add_string_value (base62_decode ("qIsPqN4LZhLd"))
//  if (raw_message.index_of (""frida:rpc"") == -1)
if (raw_message.index_of (base62_decode ("BJ91HpAN1Vp4Pua")) == -1)
//  if (type == null || type != "frida:rpc")
if (type == null || type != base62_decode ("qIsPqN4LZhLd"))

2.re.frida.server目录名混淆文件路径:frida-core/server/server.vala

检测原理:frida-server在运行时,会创建一个属于自己的工作目录。这个目录被硬编码为re.frida.server,app可以通过maps发现Frida的文件夹特征。

//  private const string DEFAULT_DIRECTORY = "re.frida.server";
private static string DEFAULT_DIRECTORY = null;//修改
private static int main (string[] args) {
DEFAULT_DIRECTORY = GLib.Uuid.string_random();//新增随机文件夹名
Environment.init ();

3.linjector-xxxx的通信管道文件名混淆文件路径:`frida-core/src/linux/frida-helper-backend-glue.c

检测原理:为了将核心的frida-agent.so文件内容注入进目标 App 的内存里,Frida会在文件系统上创建一个临时的“通信管道”,命名为`linjector-xxxx,可以通过fd目录看到相关文件描述符信息和实际的资源路径。

// self->fifo_path = g_strdup_printf ("%s/linjector-%u", self->temp_path, self->id);
self->fifo_path = g_strdup_printf ("%s/%p%u"self->temp_path, self ,self->id);//将其名字命名为app内存指针+id的组合,每次都会不同

4.frida-agent-<arch>.so文件名混淆文件路径:frida-core/src/linux/linux-host-session.vala

检测原理:这个文件打包了Frida的核心功能代码,但是需要注入到 App 的进程空间里才能开始工作。它会先释放到前面所提到的re.frida.server目录中,然后加载进去后会在应用的maps中映射其在内存中的地址。

//  agent = new AgentDescriptor (PathTemplate ("frida-agent-<arch>.so"),
var random_prefix = GLib.Uuid.string_random();
agent = new AgentDescriptor (PathTemplate (random_prefix + "-<arch>.so"),//修改命名模板
    new Bytes.static (blob32.data),
    new Bytes.static (blob64.data),
    new AgentResource[] {
        //  new AgentResource ("frida-agent-arm.so", new Bytes.static (emulated_arm.data), tempdir),
        //  new AgentResource ("frida-agent-arm64.so", new Bytes.static (emulated_arm64.data), tempdir),
        new AgentResource (random_prefix + "-arm.so"new Bytes.static (emulated_arm.data), tempdir),
        new AgentResource (random_prefix + "-arm64.so"new Bytes.static (emulated_arm64.data), tempdir),//修改生成so的名字
    },

5.frida_agent_main入口函数名、线程名、导出符号名和其他相关字符串混淆检测原理:应用可以通过解析加载到内存中的 so 文件获取frida_agent_main入口点字符串这个特征,或其它包含 frida 和frida相关的字符串、符号名以及so创建的线程名称字符串。


相关线程,字符串

gum-js-loop线程是 Frida 的JavaScript 引擎,主要用来执行js脚本;

gmain线程负责处理Frida 自身大量的内部任务,如与外界 frida-server 的通信、I/O 操作、定时器等。

gdbus是负责处理D-Bus 通信的线程。Frida 客户端和 frida-server之间的默认通信方式是 D-Bus,当然也可以直接通过tcp连接绕过。

"FridaScriptEngine"我看有的patch对这个字符串进行了替换,但是在我修改的14.2.18版本中没有搜索到这个字符串,所以我没有做替换。

"GLib-GIO"是 Frida 所依赖的一个底层库的名字,它是 GLib 库的一部分,主要提供一些网络、文件和D-Bus 通信。Frida 的通信功能建立在它之上。

"GDBusProxy"是 GLib-GIO 库里的一个的名字。通过创建一个GDBusProxy对象能够实现使用D-Bus和另一个程序通信。

"GumScript"是 Frida 内部代表一个被加载的 JS 脚本的类名。当 Frida 加载一段 hook 脚本时,Frida 的FridaScriptEngine会创建一个GumScript对象来包裹和管理这段脚本代码。

首先是针对so文件的修改,Frida会针对app注入so文件进行相关的控制,so文件可以直接用脚本批量修改。

通过Python脚本修改so文件需要lief库,需要安装一下pip install lief --break-system-packages

新增脚本anti-anti-frida.py,文件路径frida-core/src/anti-anti-frida.py

# 新增文件
import lief
import sys
import random
import os
if __name__ == "__main__":
    input_file = sys.argv[1]
    print(f"[*] Patch frida-agent: {input_file}")
    random_name = "".join(random.sample("ABCDEFGHIJKLMNOPQRSTUVWXYZ"5))
    print(f"[*] Patch `frida` to `{random_name}``")
    binary = lief.parse(input_file)
    if not binary:
        exit()
    # 符号名混淆
    for symbol in binary.symbols:
        # 将所有入口函数改为通用的main
        if symbol.name == "frida_agent_main":
            symbol.name = "main"
        
        # 将所有的带字符串Frida、FRIDA和frida的符号名替换为随机符号名
        if "frida" in symbol.name.lower():
            # 同时替换所有可能的大小写形式
            symbol.name = symbol.name.replace("frida", random_name)
            symbol.name = symbol.name.replace("Frida", random_name)
            symbol.name = symbol.name.replace("FRIDA", random_name)
    # 通过节表查找相关字符串进行替换
    all_patch_string = ["GLib-GIO""GDBusProxy""GumScript"]
    for section in binary.sections:
        if section.name == ".rodata":
            print(f"[*] Scanning section: {section.name}")
            for patch_str in all_patch_string:
                addr_all = section.search_all(patch_str)
                if addr_all:
                    random_chars = "".join(random.sample("abcdefghijklmnopqrstuvwxyz"len(patch_str)))
                    print(f"[*] Found `{patch_str}` at {len(addr_all)} locations, patching... -> `{random_chars}`" )
                    binary.patch_address(addr_all[0], [ord(c) for c in random_chars])
    binary.write(input_file)
    # 线程名混淆
    # gum-js-loop thread
    random_name_gum = "".join(random.sample("abcdefghijklmnopqrstuv"11))
    print(f"[*] Patch `gum-js-loop` to `{random_name_gum}`")
    os.system(f"sed -b -i 's/gum-js-loop/{random_name_gum}/g' {input_file}")
    # gmain thread
    random_name_gmain = "".join(random.sample("abcdefghilmnpqrstuv"5))
    print(f"[*] Patch `gmain` to `{random_name_gmain}`")
    os.system(f"sed -b -i s/gmain/{random_name_gmain}/g {input_file}")
    # gdbus thread
    random_name_gdbus = "".join(random.sample("abcdefghijklmnopqrs"5))
    print(f"[*] Patch `gdbus` to `{random_name_gdbus}`")
    os.system(f"sed -b -i s/gdbus/{random_name_gdbus}/g {input_file}")
    print("[*] All patches applied successfully!")

其次是针对代码中frida_agent_main的修改,因为so文件的入口函数以及改名字了,所以调用端也需要使用修改后的名字。其次由于部分代码作为服务端打包进了so文件,so文件结构能够使用工具批量修改,但是有一部分调用端编译以后直接就生成了机器码,很难进行批量修改。所以一部分调用端代码通过手动修改。

void * main_func_symbol;
//  var main_func_found = module.symbol ("frida_agent_main", out main_func_symbol);
var main_func_found = module.symbol ("main"out main_func_symbol);//修改
assert_true (main_func_found);
main_impl = (AgentMainFunc) main_func_symbol;

var winjector = injector as Winjector;
//  var id = yield winjector.inject_library_resource (pid, agent, "frida_agent_main", t.remote_address, cancellable);
var id = yield winjector.inject_library_resource (pid, agent, "main", t.remote_address, cancellable);//修改
injectee_by_pid[pid] = id;

var stream_request = Pipe.open (t.local_address, cancellable);
//  var id = yield qinjector.inject_library_resource (pid, agent_desc, "frida_agent_main", t.remote_address,
//   cancellable);
var id = yield qinjector.inject_library_resource (pid, agent_desc, "main", t.remote_address, cancellable);//修改
injectee_by_pid[pid] = id;

uint id;
//  string entrypoint = "frida_agent_main";
string entrypoint = "main";//修改
var linjector = injector as Linjector;

private async uint inject_agent (uint pid, string remote_address, Cancellable? cancellable) throws Error, IOError {
    uint id;
    //  unowned string entrypoint = "frida_agent_main";
    unowned string entrypoint = "main";//修改

void * main_func_symbol;
//  var main_func_found = container.module.symbol ("frida_agent_main", out main_func_symbol);
var main_func_found = container.module.symbol ("main", out main_func_symbol);//修改
assert (main_func_found);
container.main_impl = (AgentMainFunc) main_func_symbol;

文件路径:frida-core/src/agent-container.vala

文件路径:frida-core/src/darwin/darwin-host-session.vala

文件路径:frida-core/src/linux/linux-host-session.vala

文件路径:frida-core/src/qnx/qnx-host-session.vala

文件路径:frida-core/src/windows/windows-host-session.vala

文件路径:frida-core/tests/test-agent.vala

文件路径:frida-core/tests/test-injector.vala

最后是针对embed-agent.sh的修改,这个脚本是 Frida 编译的一部分。作用是获取编译好的一些需要打包的相关文件,打包到frida-server中,在这里使用anti-anti-frida.py脚本对so文件中字符串内容进行清洗替换。

resource_compiler="$7"
resource_config="$8"
custom_script="$output_dir/../../../../frida-core/src/anti-anti-frida.py" # 新增,定义脚本目录
priv_dir="$output_dir/frida-agent@emb"
case $host_os in
  macos|ios)
    if [ -z "$LIPO" ]; then
      echo "LIPO not set"
      exit 1
    fi
    ;;
esac
mkdir -p "$priv_dir"
collect_generic_agent ()
{
  embedded_agent="$priv_dir/frida-agent-$2.so"
  if [ -f "$1" ]; then
    cp "$1" "$embedded_agent" || exit 1
  else
    touch "$embedded_agent"
  fi
  # 新增,传入打包的so文件进行清洗(非Windows平台)
  if [ -f "$custom_script" ]; then
    python3 "$custom_script" "$embedded_agent"
  fi
  # 新增结束
  embedded_agents+=("$embedded_agent")
}
case $host_os in
  macos|ios)
    embedded_agent="$priv_dir/frida-agent.dylib"
    if [ -f "$agent_modern" -a -f "$agent_legacy" ]; then
      "$LIPO" "$agent_modern" "$agent_legacy" -create -output "$embedded_agent" || exit 1
    elif [ -f "$agent_modern" ]; then
      cp "$agent_modern" "$embedded_agent" || exit 1
    elif [ -f "$agent_legacy" ]; then
      cp "$agent_legacy" "$embedded_agent" || exit 1
    else
      echo "At least one agent must be provided"
      exit 1
    fi
    # 新增,传入打包的so文件进行清洗(Windows平台)
    if [ -f "$custom_script" ]; then
      python3 "$custom_script" "$embedded_agent"
    fi
    # 新增结束
    exec "$resource_compiler" --toolchain=apple -c "$resource_config" -o "$output_dir/frida-data-agent" "$embedded_agent"
    ;;
  qnx)

文件路径:frida-core/src/embed-agent.sh

6. 异常处理修改文件路径:frida-core/src/droidy/droidy-client.vala

原因:系统的一些底层服务可能会无意中向 Frida 正在监听的通道发送OPEN,CLSE,WRTE等指令(Frida的协议中不会包含这些指令)。原来的代码逻辑是,当Frida收到不在协议范围内的指令时,认为这是一个不该出现的情况,于是立即抛出一个“协议错误” (throw new Error.PROTOCOL),然后导致程序中断或崩溃。

case "SYNC":
case "CNXN":
case "AUTH":
case "OPEN":
case "CLSE":
case "WRTE":
    //  throw new Error.PROTOCOL ("Unexpected command");
    break;//修改,终止执行但不抛出异常
default:
    var length = parse_length (command_or_length);

7.内部程序名混淆文件路径:frida-core/src/frida-glue.cfrida-gum/gum/gum.c

检测原理:Frida 的核心引擎 Gum 在初始化时,会把自己内部的程序名设置为"frida"(跟安卓系统中的程序名不同,属于内部程序名,同一进程内才可以获取到),虽然安卓没有直接的接口查询,但是有可能通过使用GLib 库提供特定函数g_get_prgname()查询到。

// frida-core/src/frida-glue.c
if (g_once_init_enter (&frida_initialized))
{
// g_set_prgname ("frida");
g_set_prgname ("schimmer");//修改程序名
if (runtime == FRIDA_RUNTIME_OTHER)

// frida-gum/gum/gum.c
#endif
  g_log_set_default_handler (gum_on_log_message, NULL);
  gum_do_init ();
  // g_set_prgname ("frida");
  g_set_prgname ("schimmer");//修改程序名
#if defined (HAVE_LINUX) && defined (HAVE_GLIBC)

三、编译测试

注意node和NDK版本

报错没有找到valac编译器

C++ compiler for the host machine: /home/schimmer/workspace/frida/frida14.2.18/frida/build/frida-android-arm-clang++ (clang 11.0.5 "Android (7155654, based on r399163b1) clang version 11.0.5 (fe6K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6S2L8X3c8J5L8$3W2V1i4K6u0W2k6$3!0G2k6$3I4W2M7$3!0#2M7X3y4W2i4K6u0W2j5$3!0E0i4K6u0r3N6r3!0G2L8r3y4Z5j5h3W2F1i4K6u0r3L8r3I4$3L8g2)9J5k6s2m8J5L8$3A6W2j5%4c8Q4x3U0k6F1j5Y4y4H3i4K6y4n7z5o6N6X3x3e0x3I4y4h3c8X3j5X3g2S2y4$3x3I4x3K6N6S2j5e0u0W2y4X3b7K6y4U0u0V1j5X3t1@1y4e0N6W2x3K6R3^5x3e0f1^5k6q4)9J5z5g2)9J5y4Y4q4#2L8%4c8Q4x3@1u0Q4x3U0V1`.
C++ linker for the host machine: /home/schimmer/workspace/frida/frida14.2.18/frida/build/frida-android-arm-clang++ ld.gold 2.27.0.20170315
frida-core/meson.build:1:0: ERROR: Unknown compiler "/home/schimmer/workspace/frida/frida14.2.18/frida/build/frida-android-arm-valac"
A full log can be found at /home/schimmer/workspace/frida/frida14.2.18/frida/build/tmp-android-arm/frida-core/meson-logs/meson-log.txt
make[1]: *** [Makefile.linux.mk:215: build/tmp-android-arm/frida-core/.frida-ninja-stamp] Error 1
make[1]: Leaving directory '/home/schimmer/workspace/frida/frida14.2.18/frida'
make: *** [Makefile:4: core-android-arm64] Error 2

安装一下试试

schimmer@jnga-20230821GW:~/workspace/frida/frida14.2.18/frida$ sudo apt install valac
[sudo] password for schimmer:
Reading package lists... Done
Building dependency tree
Reading state information... Done
valac is already the newest version (0.48.6-0ubuntu1).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

已经存在了,修改前编译过一次没有出问题,但是编译完成后apt安装过一次valac,应该是起冲突了,sudo apt purge valac libgee-0.8-dev卸载掉后make clean后再编译。

报错不认识ArrayList这个类型

ArrayList来自于Gee 库,但在代码中没有明确指定它的命名空间,在frida-core/lib/interfaces/session.vala文件中ArrayList 前面加上 Gee.就行了。

报错

注册一个 D-Bus 对象时,必须为这个对象的接口(Interface)添加一个[DBus (name = "...")]的注解,来明确它在 D-Bus 服务上的名称。看了下代码是在frida-core/lib/interfaces/session.vala中添加解码函数时把函数插在了[DBus (name = "re.frida.HostSession14")]public interface HostSession : Object中间导致的,把[DBus (name = "re.frida.HostSession14")]移动到public interface HostSession : Object的头上即可。

编译完成

修改的字符串也打印出来了,接下来去frida/build/frida-android-arm64/bin找到server push进手机测试一下。注意给权限。

测试

测试了下之前存在反检测那个app直接能附加进去了,但是发现一个问题,在交互界面执行eixt退出的时候,会卡住阻塞在那,没办法正常退出,而且强制Ctrl C退出后,再次附加直接无法附加,并且所有app都点不开,但是手机重启后可以附加,感觉通信交互出问题了?还是程序崩溃了?查看端口发现,Ctrl C虽然终止了交互终端,但是端口仍在监听,并且一直没有执行关闭,感觉是通信问题。

看了一下‘’frida-core/lib/interfaces/session.vala文件源码,发现添加的给frida:rp字符串base62解码的函数,在字符串解码时没有获取到正确字符串,而且读取时可能会破坏内存,导致exit时frida-sever崩溃。

原因是vala语言编译最后会编译为C代码,C中string字符串实际上是指向内存中连续字符的指针,最终结束依赖于字符串末尾的终止符"\0",没有终止符就会一直处理直到遇到终止符,会导致内存越界。

最后在return (string) reversed_bytes.to_array();之前通过reversed_bytes.add (0);增加一个终止符。

最后重新编译,push进手机。

未魔改的直接注入:

魔改版注入:

某些大厂APP还是能检测到,技术有限,主要是跟着别人的思路梳理一遍Frida的常见检测点以及绕过,感谢大佬的patch。

四、参考

Frida自编译

https://zhuanlan.zhihu.com/p/604566400

记录一次成功的frida编译

https://blog.csdn.net/xiaoxin_OK/article/details/127911849

Frida反调试之编译与魔改Frida16.7.19

https://bbs.kanxue.com/thread-288593.htm

StrongR Frida特征魔改 - 佛光普照

https://tcc0lin.github.io/posts/strongr-frida%E7%89%B9%E5%BE%81%E9%AD%94%E6%94%B9/

GitHub - Ylarod/Florida: 基础反检测 frida-server / Basic anti-detection frida-server

https://github.com/Ylarod/Florida

Patchs/strongR-frida/frida-core at master · hluwa/Patchs · GitHub

https://github.com/hluwa/Patchs/tree/master/strongR-frida/frida-core

看雪ID:schimmer

https://bbs.kanxue.com/user-home-1032419.htm

*本文为看雪论坛优秀文章,由 schimmer 原创,转载请注明来自看雪社区

看雪·第九届安全开发者峰会(SDC 2025)

# 往期推荐

无"痕"加载驱动模块之傀儡驱动 (上)

为 CobaltStrike 增加 SMTP Beacon

隐蔽通讯常见种类介绍

buuctf-re之CTF分析

物理读写/无附加读写实验

球分享

球点赞

球在看

点击阅读原文查看更多

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Frida 源码修改 绕过检测 反调试 移动安全 逆向工程 Frida Source Code Modification Bypass Detection Anti-Debugging Mobile Security Reverse Engineering
相关文章