genliese 发表于 2021-2-1 10:33

Frida调试环境搭建

本帖最后由 genliese 于 2021-2-2 20:17 编辑

## 1. 前言

- 目标:使用VSCode”调试“frida脚本
- 环境
      - 设备:Pixel 3 Android9 180720.030
      - frida:14.2.6
      - Node.js:14.15.4
      - npm:6.14.10
      - Visual Studio Code 1.52.1 (user setup)
      - VSCode使用的是IntelliJ IDEA Keybindings
      - 2021/1/31

我们使用`TypeScript`编写frida脚本,再通过`frida-compile`把`TypeScript`编译成`JavaScript`代码。

- 使用`TypeScript`的好处
      - 自动补全
      - 类型检查
      - 内联文档
      - 重构工具
      - ......

首先介绍搭建`TypeScript`调试环境,再具体介绍Frida调试环境的配置

## 2. 搭建TypeScript调试环境
### 2.1 搭建TypeScript开发环境
#### 2.1.1 安装Node.js和npm

**Node.js下载**
https://nodejs.org/en/download/

npm和Node.js是捆绑在一起的

**配置npm**

npm使用的默认源(`registry`)是`https://registry.npmjs.org`,在国外访问很慢,就算挂代{过}{滤}理访问也慢,所以直接替换成国内的源`http://registry.npm.taobao.org`,操作如下:

得到配置文件路径
`npm config get`

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/User_Config.png)

修改用户配置文件

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/registry_taobao.png)

- 配置文件优先级:
      -   per-project config file (/path/to/my/project/.npmrc)
      -   per-user config file (~/.npmrc)
      -   global config file ($PREFIX/etc/npmrc)
      -   npm builtin config file (/path/to/npm/npmrc)

#### 2.1.2 建立项目目录

使用以下命令创建项目的目录:

```C
mkdir ts3
cd ts3
mkdir src
mkdir dist
```

建立好的目录如下:

```C
ts3
├─dist
└─src
```

#### 2.1.3 初始化 NPM

在项目的根目录下,执行下面的命令:

```C
npm init -y
```

现在项目结构如下:

```C
ts3
├─dist
└─src
└─package.json
```

(https://docs.npmjs.com/cli/v6/configuring-npm/package-json)

#### 2.1.4 安装TypeScript

在项目的根目录下,执行下面的命令:

```C
npm install -g typescript
```

加了参数`-g`,会在全局环境安装`TypeScript`,不加只会在当前工程下安装`TypeScript`

#### 2.1.5 创建并配置tsconfig.json

在项目的根目录下,执行下面的命令:

```C
tsc --init
```

现在项目结构如下:

```C
ts3
├─dist
└─src
└─package.json
└─tsconfig.json
```

(https://www.typescriptlang.org/docs/handbook/tsconfig-json.html)

在 `tsconfig.json` 中取消下面属性项的注释,并修改其属性的值:

> 这样设置之后,我们在 `./src` 中编码 `.ts` 文件,`.ts` 文件编译成 `.js` 后,输出到 `./dist` 中。

```C
"outDir": "./dist",
"rootDir": "./src",
```

#### 2.1.6 Hello TypeScript

将下面代码复制到`./src/index.ts`中:

```C
const hello: string = 'hello, Genliese';
console.log(hello);
```

在项目的根目录下,执行下面的命令:

> `tsc` 是编译命令,详情查看:`https://www.tslang.cn/docs/handbook/typescript-in-5-minutes.html`
>
> `tsc` 的编译选项,详情查看:`https://www.tslang.cn/docs/handbook/compiler-options.html`

```C
//编译
tsc
//执行
node ./dist/index.js
```

执行结果如下:

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/node_result.png)

#### 2.1.7 使用自动实时编译

> 手动编译还是比较麻烦,如果能够保存代码后,能自动编译就好了。
>
> 详情查看:`https://go.microsoft.com/fwlink/?LinkId=733558`

`Ctrl + F9` 运行构建任务,将显示以下选项:

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/build_task.png)

选择 `tsc: watch - tsconfig.json` ,回车运行之后,编辑的代码保存之后,就会自动编译。

### 2.2 代码检查

> 代码检查主要是用来发现代码错误和统一代码风格。
>
> 详情查看:`https://ts.xcatliu.com/engineering/lint.html`

#### 2.2.1 安装ESLint

`ESLint` 可以安装在当前项目中或全局环境下,因为代码检查是项目的重要组成部分,所以我们一般会将它安装在当前项目中(**也可以安装在全局,因为`package.json`中记录有依赖库,别人下载项目后,直接`npm install`即可安装所有依赖库**)。可以运行下面的脚本来安装:

```C
npm install eslint --save-dev
```

由于 `ESLint` 默认使用 [`Espree`](https://github.com/eslint/espree) 进行语法解析,无法识别 `TypeScript` 的一些语法,故我们需要安装 `typescript-eslint-parser`,替代掉默认的解析器,别忘了同时安装 `typescript`:

```C
npm install typescript typescript-eslint-parser --save-dev
```

由于 `typescript-eslint-parser` 对一部分 `ESLint` 规则支持性不好,故我们需要安装 `eslint-plugin-typescript`,弥补一些支持性不好的规则。

```C
npm install eslint-plugin-typescript --save-dev
```

现在项目结构如下:

```C
ts3
├─dist
└─node_modules
└─src
└─package-lock.json
└─package.json
└─tsconfig.json
```

package-lock.json是锁定依赖库的版本
(https://docs.npmjs.com/cli/v6/configuring-npm/package-lock-json)

#### 2.2.2 创建配置文件 .eslintrc.js

> `ESLint` 需要一个配置文件来决定对哪些规则进行检查,配置文件的名称一般是 `.eslintrc.js` 或 `.eslintrc.json`。

> 当运行 `ESLint` 的时候检查一个文件的时候,它会首先尝试读取该文件的目录下的配置文件,然后再一级一级往上查找,将所找到的配置合并起来,作为当前被检查文件的配置。

在项目的根目录下,执行下面的命令:

```C
//创建配置文件
./node_modules/.bin/eslint --init
```

按需求,选择相应的选项:

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/eslint_config.png)

图中出现了`WARN`,编辑`package.json`修改对应字段:

```C
"description": "default",
"repository": {
    "type": "git",
    "url": "none"
},
```

现在项目结构如下:

```C
ts3
├─dist
└─node_modules
└─src
└─.eslintrc.js
└─package-lock.json
└─package.json
└─tsconfig.json
```

编辑 `.eslintrc.js`,增加 `parser: 'typescript-eslint-parser',` 替换掉默认的解析器,使之识别 `TypeScript` 的一些语法,如下面所示:

```C
module.exports = {
parser: 'typescript-eslint-parser',
env: {
    es6: true,
    node: true,
},
extends: 'airbnb-base',
globals: {
    Atomics: 'readonly',
    SharedArrayBuffer: 'readonly',
},
parserOptions: {
    ecmaVersion: 2018,
    sourceType: 'module',
},
rules: {
},
};
```

#### 2.2.3 在 VSCode中集成ESLint检查

在编辑器中集成 `ESLint` 检查,可以在开发过程中就发现错误,极大的增加了开发效率。

要在 `VSCode` 中集成 `ESLint` 检查,我们需要先安装 `ESLint` 插件,点击「扩展」按钮,搜索 `ESLint`,然后安装即可。

`VSCode` 中的 `ESLint` 插件默认是不会检查 `.ts` 后缀的,需要在「文件 => 首选项 => 设置」中

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/eslint_validate.png)

添加以下配置:

```C
{
"eslint.validate": [
    "typescript"
]
}
```

将下面代码复制到`./src/index.ts`中:

```C
let num: number = 1;
if (num == 2) {
console.log(num);
}
```

现在项目结构如下:

```C
ts3
├─dist
└─node_modules
└─src
└─index.ts
└─.eslintrc.js
└─package-lock.json
└─package.json
└─tsconfig.json
```

现在编辑器,应该会提示 `4` 个错误:

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/four_errors.png)

我们按照错误提示,修改成正确的代码风格(在错误处按`Alt+Enter`):

> `console.log` 一般是在调试阶段使用,发布正式版本时,应该移除。所以这里没有提示红色的致命错误,而是使用了警告。

#### 2.2.4 错误纠正

将下面代码复制到`./src/index.ts`中:

```C
import Cat from './Cat';

const kitty: Cat = new Cat('kitty');
kitty.say();
```

将下面代码复制到`./src/Cat.ts`中:

```C
export default class Cat {
private name: string;

constructor(name: string) {
    this.name = name;
}

say() {
    console.log(this.name);
}
}
```

现在项目结构如下:

```C
ts3
├─dist
└─node_modules
└─src
└─Cat.ts
└─index.ts
└─.eslintrc.js
└─package-lock.json
└─package.json
└─tsconfig.json
```

上述代码复制粘贴,保存之后,会提示这样的错误:

**错误1**
```C
Unable to resolve path to module './Cat'.eslint(https://github.com/benmosher/eslint-plugin-import/blob/v2.22.1/docs/rules/no-unresolved.md)
```

解决办法是使用 [`eslint-import-resolver-alias`](https://github.com/johvin/eslint-import-resolver-alias) ,先安装依赖,执行下面的命令:

```C
npm install eslint-plugin-import eslint-import-resolver-alias --save-dev
```

然后,在 `.eslintrc.js` 配置中,编辑成如下代码:

```C
module.exports = {
parser: 'typescript-eslint-parser',
env: {
    browser: true,
    es6: true,
},
extends: 'airbnb-base',
globals: {
    Atomics: 'readonly',
    SharedArrayBuffer: 'readonly',
},
parserOptions: {
    ecmaVersion: 2018,
    sourceType: 'module',
},
rules: {
},
settings: {
//import/resolver起的作用
    'import/resolver': {
      alias: {
      map: [
          ['@', './src'],
      ],
      extensions: ['.ts'],
      },
    },
},
};
```

**错误2**
```C
Missing file extension "ts" for "./Cat"eslint(https://github.com/benmosher/eslint-plugin-import/blob/v2.22.1/docs/rules/extensions.md)
```

在 `.eslintrc.js` 配置中,编辑成如下代码:

```C
module.exports = {
parser: 'typescript-eslint-parser',
env: {
    browser: true,
    es6: true,
},
extends: 'airbnb-base',
globals: {
    Atomics: 'readonly',
    SharedArrayBuffer: 'readonly',
},
parserOptions: {
    ecmaVersion: 2018,
    sourceType: 'module',
},
rules: {
//import/extensions起的作用
    'import/extensions': [
      2, // 0 = off, 1 = warn, 2 = error
      'ignorePackages',
      {
      ts: 'never',
      },
    ],
},
settings: {
    'import/resolver': {
      alias: {
      map: [
          ['@', './src'],
      ],
      extensions: ['.ts'],
      },
    },
},
};
```

### 2.3 调试TypeScript

> 如何 `F5` 开始调试 `TypeScript` ,并且还具备断点调试功能,答案是,使用 `TS-node`。
>
> 详情查看:`https://github.com/TypeStrong/ts-node`

在项目的根目录下,执行下面的命令:

```C
npm install -D ts-node
```
Run->Add Configuration

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/debug_config.png)

然后修改成如下配置:

```C
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch Program",
      "runtimeArgs": [
      "-r",
      "ts-node/register"
      ],
      "args": [
      "${workspaceFolder}/src/index.ts"
      ]
    }
]
}
```

按 `F5` 开始愉快的调试吧,`F9` 是添加断点:

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/breakpoint.png)

如果出现`unbound breakpoint`的问题,修改`debug.javascript.usePreview`为false即可

## 3. Frida调试环境的配置

Frida的安装参考(https://frida.re/docs/installation/)

我们已经搭建好了TypeScript调试环境,下面继续Frida调试环境的配置,可以参考(git://github.com/oleavr/frida-agent-example.git)项目的配置文件。

**frida-agent-example的package.json文件**

```C
{
"name": "frida-agent-example",
"version": "1.0.0",
"description": "Example Frida agent written in TypeScript",
"private": true,
//入口函数
"main": "agent/index.ts",
//下面的脚本可以用npm run prepare、npm run build和npm run prepar watch运行
"scripts": {
    "prepare": "npm run build",
    "build": "frida-compile agent/index.ts -o _agent.js -c",
    "watch": "frida-compile agent/index.ts -o _agent.js -w"
},
//依赖库,可以在项目根目录用npm install进行安装
"devDependencies": {
    "@types/frida-gum": "^16.2.0",
    "@types/node": "^14.14.10",
    "frida-compile": "^10.0.0"
}
}
```

也就是我们可以需要将以下内容拷贝到自己的`package.json`文件中,然后在项目根目录下执行`npm install`安装下面的依赖

```C
{
//下面的脚本可以用npm run prepare、npm run build和npm run prepar watch运行
"scripts": {
    "prepare": "npm run build",
    "build": "frida-compile src/index.ts -o dist/index.js -c",
    "watch": "frida-compile src/index.ts -o dist/index.js -w"
},
//依赖库,可以在项目根目录用npm install进行安装
"devDependencies": {
    "@types/frida-gum": "^16.2.0",
    "@types/node": "^14.14.10",
    "frida-compile": "^10.0.0"
}
}
```

## 4. 开始调试Frida脚本

**注意点**
- 没有找到方法直接用VSCode进行附加调试,而是用chrome的**DelTools工具**进行的附加调试,用vscode编写`TypeScript`语言的Frida代码,并结合实时编译、DelTools工具自动监测脚本是否修改和Frida自动监测脚本是否修改的特性,可以达到比较理性的调试效果
      - 实时编译:通过`frida-compile src/index.ts -o dist/index.js -w`命令可以实时把成`TypeScript`编译成`JavaScript`代码
                - Frida不支持直接注入`TypeScipt`
      - DelTools工具自动监测脚本是否修改:如果修改了,自动更新被调试的脚本
      - Frida自动监测脚本是否修改:如果修改了,注入的脚本也会跟着修改
      

**1.开启实时编译**

`Ctrl+F9`选择`npm: watch`

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/open_real_time_compilation.png)

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/real_time_compilation_success.png)

**2.启动frida-server**

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/frida_server.png)

**3.注入脚本**

`.\frida.exe -U -f com.android.chrome --debug --runtime=v8 --no-pause -l "...\ts3\dist\index.js"`

监听端口为本地的`9229`

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/inject_script.png)

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/inject_script_result.png)

**4.chrome的DelTools工具进行附加调试**

打开chrome,输入`chrome://inspect`

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/open_devtools.png)

添加连接——本地端口`9229`

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/add_connection.png)

在`Sources`界面,`Ctrl+P`选择要调试的脚本

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/sources_select_script.png)

格式化脚本

格式化前
![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/script_formatted_before.png)

格式化后
![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/script_formatted_after.png)

**5.Ctrl+B下断点**

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/add_breakpoint.png)

**6.vscode修改脚本**

实时编译
![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/modify_real_time.png)

DelTools工具自动监测脚本是否修改
![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/modify_devtools.png)

Frida自动监测脚本是否修改
![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/modify_frida.png)

## 5. 问题记录
### 5.1 pycharm调试frida脚本

参考[正确配置 Visual Studio Code 开发和调试 TypeScript](https://juejin.cn/post/6844903813887836173#heading-11)中用pycharm调试frida脚本,PyCharm给js文件和ts文件下断点都没有反应,不知道是为什么

### 5.2 chrome的DelTools工具显示不出来文件

如果chrome的DelTools工具显示不出来文件,那么点击`Configure`配置,`Enable port forwarding`,然后再取消

![](https://genliesephotos.genliese.cn/Markdown/Setup_Frida_Debug_Env/devtools_cannot_show_file.png)

### 5.3 chrome的DelTools工具显示的脚本名是script1

这是因为你用api的方式注入的脚本,是直接用变量输入的code,没有文件名,所以chrome默认取名为script1

### 5.4 get_usb_device有时候会找不到设备 device not found

加延迟就行了`get_usb_device(10)`

### 5.5 如果frida注入脚本,导致app退出,可以尝试重启手机

## 6. 参考

[正确配置 Visual Studio Code 开发和调试 TypeScript](https://juejin.cn/post/6844903813887836173#heading-11)

(https://bbs.pediy.com/thread-265160-1.htm)

## 7. 附件

**工程文件:**
链接:https://pan.baidu.com/s/18qlGc1OFSOu4Y29mEmQY3g
提取码:abco

genliese 发表于 2021-3-3 07:58

本帖最后由 genliese 于 2021-3-30 20:17 编辑

小木曾雪菜 发表于 2021-2-25 11:29
又没有不用typescript的方法?
修正一下,在这环境里直接用js也有代码内联文档和代码补全

阿傑 发表于 2021-2-1 11:33

感谢分享,学习下

love514415 发表于 2021-2-1 12:30

感谢分享

Rolanju 发表于 2021-2-1 12:34

支持楼主分享

我与菜鸟 发表于 2021-2-1 14:12

期待pycharm调试frida脚本解决{:1_918:}

leesommer 发表于 2021-2-1 15:52

感谢分享

coolboyxdl 发表于 2021-2-1 18:54

很好 哈哈

MFC 发表于 2021-2-1 22:08

得多请几天假,好好学习最近各路大神的文章

Alsser 发表于 2021-2-1 22:13

受教了,感谢楼主分享!

yanxunchan 发表于 2021-2-1 22:45

谢谢分享,mark等来学习下
页: [1] 2 3 4 5
查看完整版本: Frida调试环境搭建