schimmer 2025-10-09 18:00 上海
看雪论坛作者ID:schimmer

🛠️ **编译环境搭建与版本适配**:文章详细列出了编译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-
📞 **通信管道和入口函数名混淆**:文章还提到了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
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:yourProxyPort和git 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
git config --global url.
"https://github.com/"
.insteadOf git:
//github
.com/
# 继续下载cd fridagit submodule update --init --recursive
# 在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-arm64schimmer@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
schimmer@DESKT-SCHIMMER:~/workspace/frida/frida12.8.0/frida$ git checkout -b master
Switched to a new branch 'master'
schimmer@DESKT-SCHIMMER:~/workspace/frida/frida12.8.0/frida$ make 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.bz2https://build.frida.re/deps/20210123/sdk-linux-x86_64.tar.bz2https://build.frida.re/deps/20210123/sdk-android-arm.tar.bz2https://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/frida$ grep -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.exeschimmer@jnga-20230821GW:~/workspace/frida/frida12.8.0/frida$ grep "toolchain_version=" releng/setup-env.shtoolchain_version=20190428schimmer@jnga-20230821GW:~/workspace/frida/frida12.8.0/frida$ grep "sdk_version=" releng/setup-env.shsdk_version=20191024 sdk_version="$sdk_version-asan"◆直接字符串搜索了一下在setup-env.sh中,时间号为20191024和20190428,确实工具链地址已经不存在了,所以下载不到会报错,直接进去把这两个下载注释掉,直接不用工具链完全重新编译试试。# 编译
make -j$(nproc) core-android-arm64
frida/build/frida-android-arm64/bin)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"))
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 ();
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的组合,每次都会不同
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的名字
},
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.sh6. 异常处理修改文件路径: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);
frida-core/src/frida-glue.c,frida-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)
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.
sudo apt purge valac libgee-0.8-dev卸载掉后make clean后再编译。◆报错不认识ArrayList这个类型ArrayList来自于Gee 库,但在代码中没有明确指定它的命名空间,在frida-core/lib/interfaces/session.vala文件中ArrayList 前面加上 Gee.就行了。◆报错[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进手机测试一下。注意给权限。测试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进手机。◆未魔改的直接注入:https://github.com/hluwa/Patchs/tree/master/strongR-frida/frida-core
看雪ID:schimmer
*本文为看雪论坛优秀文章,由 schimmer 原创,转载请注明来自看雪社区
看雪·第九届安全开发者峰会(SDC 2025)# 往期推荐
无"痕"加载驱动模块之傀儡驱动 (上)
为 CobaltStrike 增加 SMTP Beacon
隐蔽通讯常见种类介绍
buuctf-re之CTF分析
物理读写/无附加读写实验
AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。
鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑