Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions _i18n/cn.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
- title: 入门指南
docs:
- home
- quickstart
- installation
- modes
- gadget
- hacking
- presentations

- title: 教程
docs:
- functions
- messages
- ios
- android

- title: 示例
docs:
- examples/windows
- examples/macos
- examples/linux
- examples/ios
- examples/android
- examples/javascript

- title: 工具
docs:
- frida-cli
- frida-ps
- frida-trace
- frida-discover
- frida-ls-devices
- frida-kill

- title: API 参考
docs:
- javascript-api
- c-api
- swift-api

- title: 其他
docs:
- best-practices
- troubleshooting
- building
- footprint

- title: 关于
docs:
- contributing
- gsoc-ideas-2015
- gsod-ideas-2023
- history
93 changes: 93 additions & 0 deletions _i18n/cn/_docs/android.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
在本教程中,我们将展示如何在您的 Android 设备上进行函数跟踪。

## 设置您的 Android 设备

在开始之前,如果您还没有 root 您的设备,您需要先 root。从技术上讲,不 root 设备也可以使用 Frida,例如通过重新打包应用以包含 frida-gadget,或使用调试器来实现相同的目的。但是,对于本介绍,我们将专注于最简单的情况:已 root 的设备。

另请注意,我们最近的大部分测试都是在运行 Android 9 的 Pixel 3 上进行的。较旧的 ROM 可能也可以工作,但是如果您遇到基本问题,例如启动应用程序时 Frida 使系统崩溃,这是由于特定于 ROM 的怪癖。我们无法在所有可能的设备上进行测试,因此我们依靠您的帮助来改进这一点。但是,如果您刚开始使用 Frida,强烈建议使用运行最新官方软件的 Pixel 或 Nexus 设备,或者软件尽可能接近 AOSP 的设备。另一种选择是使用模拟器,最好使用 Google 提供的适用于 arm 或 arm64 的 Android 9 模拟器镜像。(x86 可能也可以工作,但测试明显较少。)

您还需要 Android SDK 中的 `adb` 工具。

首先,从我们的 [releases 页面](https://github.com/frida/frida/releases)下载适用于 Android 的最新 `frida-server` 并解压缩。

{% highlight bash %}
$ adb shell getprop ro.product.cpu.abilist # check your device cpu type

$ unxz frida-server.xz
{% endhighlight %}

现在,让我们在您的设备上运行它:

{% highlight bash %}
$ adb root # might be required
$ adb push frida-server /data/local/tmp/
$ adb shell "chmod 755 /data/local/tmp/frida-server"
$ adb shell "/data/local/tmp/frida-server &"
{% endhighlight %}

某些应用可能能够检测到 frida-server 的位置。将 frida-server 二进制文件重命名为随机名称,或将其移动到另一个位置(如 /dev)可能会奏效。

对于最后一步,请确保以 root 身份启动 frida-server,即如果您在已 root 的设备上执行此操作,您可能需要 *su* 并从该 shell 运行它。

<div class="note info">
<h5>生产版本上的 adb</h5>
<p>
如果在运行 <code>adb root</code> 后收到 <code>adbd cannot run as root in production builds</code><br>您需要在每个 shell 命令前加上 <code>su -c</code>。例如:
<code>adb shell "su -c chmod 755 /data/local/tmp/frida-server"</code>
</p>
</div>

接下来,确保 `adb` 可以看到您的设备:

{% highlight bash %}
$ adb devices -l
{% endhighlight %}

这也将确保 adb 守护进程在您的桌面上运行,这允许 Frida 发现并与您的设备通信,无论您是通过 USB 还是 WiFi 连接它。

## 快速冒烟测试

现在,在您的桌面上,是时候确保基础功能正常工作了。运行:

{% highlight bash %}
$ frida-ps -U
{% endhighlight %}

这应该会给您一个类似于以下的进程列表:

{% highlight bash %}
PID NAME
1590 com.facebook.katana
13194 com.facebook.katana:providers
12326 com.facebook.orca
13282 com.twitter.android
{% endhighlight %}

太棒了,我们可以开始了!

## 跟踪 Chrome 中的 open() 调用

好了,让我们找点乐子。在您的设备上启动 Chrome 应用,然后返回桌面并运行:

{% highlight bash %}
$ frida-trace -U -i open -N com.android.chrome
Uploading data...
open: Auto-generated handler …/linker/open.js
open: Auto-generated handler …/libc.so/open.js
Started tracing 2 functions. Press Ctrl+C to stop.
{% endhighlight %}

现在只需玩一下 Chrome 应用,您应该开始看到 `open()` 调用飞入:

{% highlight bash %}
1392 ms open()
1403 ms open()
1420 ms open()
{% endhighlight %}

您现在可以在阅读 `man open` 时实时编辑上述 JavaScript 文件,并开始越来越深入地研究您的 Android 应用。

## 构建您自己的工具

虽然像 *frida*、*frida-trace* 等 CLI 工具绝对非常有用,但有时您可能希望利用强大的 [Frida API](/docs/javascript-api/) 构建自己的工具。为此,我们建议阅读有关 [Functions](/docs/functions) 和 [Messages](/docs/messages) 的章节,并且在任何看到 `frida.attach()` 的地方,只需将其替换为 `frida.get_usb_device().attach()`。
71 changes: 71 additions & 0 deletions _i18n/cn/_docs/best-practices.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
本节旨在包含使用 Frida 时经常遇到的最佳实践和陷阱。

### 字符串分配 (UTF-8/UTF-16/ANSI)

阅读文档后,人们可能会认为分配/替换字符串就像这样简单:

{% highlight javascript %}
onEnter(args) {
args[0].writeUtf8String('mystring');
}
{% endhighlight %}

但是,这可能是不可能的,因为指向的字符串可能:

- 驻留在“只读数据”段中,该段作为只读映射到进程的地址空间;
- 比那里的字符串长,因此 *writeUtf8String()* 会导致缓冲区溢出并可能破坏不相关的内存。

即使您可以通过使用 *Memory.protect()* 解决前一个问题,也有一个更好的解决方案:分配一个新字符串并替换参数。

但是有一个陷阱:*Memory.allocUtf8String()* 返回的值必须保持活动状态——一旦 JavaScript 值被垃圾回收,它就会被释放。这意味着它至少需要在函数调用期间保持活动状态,在某些情况下甚至更长;确切的语义取决于 API 的设计方式。

考虑到这一点,执行此操作的可靠方法是:

{% highlight javascript %}
onEnter(args) {
const buf = Memory.allocUtf8String('mystring');
this.buf = buf;
args[0] = buf;
}
{% endhighlight %}

它的工作原理是 *this* 绑定到一个每个线程和每个调用都存在的对象,您存储在那里的任何东西都将在 *onLeave* 中可用,这甚至在递归的情况下也有效。这样您就可以在 *onEnter* 中读取参数,并在稍后的 *onLeave* 中访问它们。这也是在函数调用期间保持内存分配处于活动状态的推荐方法。

如果函数保留指针并在函数调用完成后也使用它,一种解决方案是像这样操作:

{% highlight javascript %}
const myStringBuf = Memory.allocUtf8String('mystring');

Interceptor.attach(f, {
onEnter(args) {
args[0] = myStringBuf;
}
});
{% endhighlight %}

### 重用参数

在 *onEnter* 回调中读取参数时,通常通过索引访问每个参数。但是当多次访问同一个参数时会发生什么?以这段代码为例:

{% highlight javascript %}
Interceptor.attach(f, {
onEnter(args) {
if (!args[0].readUtf8String(4).includes('MZ')) {
console.log(hexdump(args[0]));
}
}
});
{% endhighlight %}

在上面的示例中,第一个参数从 *args* 数组中获取了两次,这就支付了两次向 *frida-gum* 查询此信息的成本。为了避免在多次需要相同参数时浪费宝贵的 CPU 周期,最好使用局部变量存储此信息:

{% highlight javascript %}
Interceptor.attach(f, {
onEnter(args) {
const firstArg = args[0];
if (!firstArg.readUtf8String(4).includes('MZ')) {
console.log(hexdump(firstArg));
}
}
});
{% endhighlight %}
176 changes: 176 additions & 0 deletions _i18n/cn/_docs/bridges.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
从 Frida 17.0.0 开始,桥接器不再与 Frida 的 GumJS 运行时捆绑在一起。您可以在 [release notes][] 中阅读更多相关信息。这意味着用户现在必须显式引入他们想要使用的桥接器。不过,Frida REPL 和 `frida-trace` 确实捆绑了所有三个桥接器,以便与现有脚本兼容。

## 目录

1. **REPL 和 frida-trace**
1. [使用纯 JavaScript](#使用纯-javascript)
1. [REPL 自动编译](#repl-自动编译)
1. [使用 frida-compile 手动编译](#使用-frida-compile-手动编译)
1. **使用 API**
1. [Python 示例](#python-示例)
1. [Go 示例](#go-示例)

## REPL 和 frida-trace

我们将使用一个简单的脚本将 `ObjC.available` 打印到屏幕上。

{% highlight javascript %}
// script.js
console.log(ObjC.available);
{% endhighlight %}

### 使用纯 JavaScript

这与以前完全一样,因为 REPL 和 frida-trace 捆绑了所有三个桥接器。

{% highlight bash %}
$ frida -p0 -l script.js
____
/ _ | Frida 17.0.5 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://frida.re/docs/home/
. . . .
. . . . Connected to Local System (id=local)
Attaching...
true
[Local::SystemSession ]->
{% endhighlight %}

### REPL 自动编译

REPL 也可以使用 `.ts` 文件:在空目录中使用 `frida-create -t agent` 来设置所需的脚手架。

### 使用 frida-compile 手动编译

您需要通过在脚本中添加行来指定要使用的桥接器(ObjC, Java, Swift):

* `import ObjC from "frida-objc-bridge";` - 用于 ObjC
* `import Swift from "frida-swift-bridge";` - 用于 Swift
* `import Java from "frida-java-bridge";` - 用于 Java

我们将重新创建上面的示例,其中我们使用纯 JavaScript 打印 `ObjC.available`。

{% highlight typescript %}
// script.ts
import ObjC from "frida-objc-bridge";

console.log(ObjC.available);
{% endhighlight %}

在空目录中初始化并安装必要的包:

{% highlight bash %}
$ frida-create -t agent
$ npm install
$ npm install frida-objc-bridge
{% endhighlight %}

然后编译代理并加载它:

{% highlight bash %}
$ frida-compile script.ts -o _agent.js -S -c
$ frida -p0 -l _agent.js
____
/ _ | Frida 17.0.5 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://frida.re/docs/home/
. . . .
. . . . Connected to Local System (id=local)
Attaching...
true
[Local::SystemSession ]->
{% endhighlight %}

## 使用 API

使用绑定提供的 API 需要几个步骤,即:

* 编写脚本
* 运行 `frida-create -t agent`
* 安装您想要的桥接器,例如 `frida-objc-bridge`
* 编写代码来编译脚本并将其加载到进程中

### Python 示例

{% highlight python %}
import frida

def on_diagnostics(diag):
print("diag", diag)

def on_message(message, data):
print(message)

compiler = frida.Compiler()
compiler.on("diagnostics", on_diagnostics)
# script is located in /tmp, so we set project root to /tmp
bundle = compiler.build("script.ts", project_root="/tmp")

session = frida.attach(0)

script = session.create_script(bundle)

script.on("message", on_message)
script.load()
{% endhighlight %}

### Go 示例

{% highlight go %}
package main

import (
"bufio"
"fmt"
"github.com/frida/frida-go/frida"
"os"
)

func main() {
comp := frida.NewCompiler()
comp.On("diagnostics", func(diag string) {
fmt.Printf("Diagnostics: %s\n", diag)
})

bopts := frida.NewCompilerOptions()
bopts.SetProjectRoot("/tmp")
bopts.SetSourceMaps(frida.SourceMapsOmitted)
bopts.SetJSCompression(frida.JSCompressionTerser)

bundle, err := comp.Build("script.ts", bopts)
if err != nil {
panic(err)
}

session, err := frida.Attach(0)
if err != nil {
panic(err)
}

script, err := session.CreateScript(bundle)
if err != nil {
panic(err)
}

script.On("message", func(message string, data []byte) {
fmt.Printf("%s\n", message)
})

script.Load()

r := bufio.NewReader(os.Stdin)
r.ReadLine()
}
{% endhighlight %}

[release notes]: /news/2025/05/17/frida-17-0-0-released/
Loading