Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
frida-snippets — 手作りのFridaサンプル | Kitploit
ツール/GitHubGitHub/iddoeldor/frida-snippets
Androidセキュリティ動的分析 (サンドボックス)iOSセキュリティリバースエンジニアリングデバッガモバイルセキュリティバイナリ解析
GitHubiddoeldor/frida-snippets

frida-snippets

手作りのFridaサンプル

リポジトリを見る
2.5k43981年前Kitploit レビュー済み

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

PRs Welcome & 出力例も提供します

目次

ネイティブ
  • Load C/C++ module
  • One time watchpoint
  • Socket activity
  • Intercept open
  • Execute shell command
  • List modules
  • Log SQLite query
  • Log method arguments
  • Intercept entire module
  • Dump memory segments
  • Memory scan
  • Stalker
  • Cpp Demangler
  • Early hook
Android
  • Binder transactions
  • Get system property
  • Reveal manually registered native symbols
  • Enumerate loaded classes
  • Class description
  • Turn WiFi off
  • Set proxy
  • Get IMEI
  • Hook io InputStream
  • Android make Toast
  • Await for specific module to load
iOS
  • OS Log
  • iOS alert box
  • File access
  • Observe class
  • Find application UUID
  • Extract cookies
  • Describe class members
  • Class hierarchy
  • Hook refelaction
  • Device properties
  • Take screenshot
  • Log SSH commands
Windows

HAHAHA. No.

Sublimeスニペット
root@kitploit:~
{
    "scope": "source.js",
    "completions": [
	{"trigger": "fridainterceptor", "contents": "Interceptor.attach(\n    ptr,\n    {\n        onEnter:function(args) {\n\n        },\n        onLeave: function(retval) {\n\n        }\n    }\n)"},
	{"trigger": "fridaperform", "contents": "function main(){\n    console.log('main()');\n}\n\nconsole.log('script loaded');\nJava.perform(main);"},
	{"trigger": "fridause", "contents": "var kls = Java.use('kls');"},
	{"trigger": "fridahex", "contents": "hexdump(\n    ptr,\n    {\n         offset: 0,\n         length: ptr_size\n     }\n);" },
	{"trigger": "fridabacktrace", "contents": "console.log('called from:\\n' +\n        Thread.backtrace(this.context, Backtracer.ACCURATE)\n        .map(DebugSymbol.fromAddress).join('\\n') + '\\n'\n);"},
	{"trigger": "fridamods", "contents": "var mods = Process.enumerateModules().filter(function(mod){\n    return mod.name.includes(\"<name>\");\n});"},
	{"trigger": "fridaexport", "contents": "Module.findExportByName(null, \"<export_name>\");"},
	{"trigger": "fridabase", "contents": "Module.findBaseAddress(name);"},
	{"trigger": "fridaoverload", "contents": "kls.method_name.overload().implementation=function(){}"}
    ]
}
Vimスニペット

略語一覧を表示するには :ab

key と <Space> を入力すると展開します

  • ~/.vimrc に追加
``` ab fridaintercept Interceptor.attach(ptr, {onEnter: function(args) {},onLeave: function(retval) {}}) ab fridabacktrace console.warn(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n'));F(3; ab fridadescribe console.log(Object.getOwnPropertyNames(Java.use('$').__proto__).join('\n\t'))F$ ```
JEB

キーボードショートカットを使用したJavaメソッドフック生成ツール

  1. curl -o ~/$JEB$/scripts/FridaCodeGenerator.py https://raw.githubusercontent.com/iddoeldor/frida-snippets/master/scripts/FridaCodeGenerator.py
  2. Javaメソッドのシグネチャにカーソルを置く
  3. Ctrl+Shift+Zを押す
  4. コードがシステムクリップボードにコピーされます (xclipを使用)

SSLキーの取得```js

var keylog_callback = new NativeCallback((ssl, line) => { send(Memory.readCString(line)); }, 'void', ['pointer', 'pointer']);

if (ObjC.available) { var CALLBACK_OFFSET = 0x2A8 if (Memory.readDouble(Module.findExportByName('CoreFoundation', 'kCFCoreFoundationVersionNumber')) >= 1751.108) { CALLBACK_OFFSET = 0x2B8 } Interceptor.attach(Module.findExportByName('libboringssl.dylib', 'SSL_CTX_set_info_callback'), { onEnter(args) { ptr(args[0]).add(CALLBACK_OFFSET).writePointer(keylog_callback) } }) } else if (Java.available) { var set_keylog_callback = new NativeFunction(Module.findExportByName('libssl.so', 'SSL_CTX_set_keylog_callback'), 'void', ['pointer', 'pointer']); Interceptor.attach(Module.findExportByName('libssl.so', 'SSL_CTX_new'), { onLeave(retval) { set_keylog_callback(retval, keylog_callback) } }) }

root@kitploit:~
<br>[⬆ トップに戻る](#table-of-contents)
	

#### CPPモジュールの読み込み```cpp
#include <iostream>
#include <string>

extern "C" {
  void* create_stdstr(char *data, int size) {
    std::string* s = new std::string();
    (*s).assign(data, size);		  
    return s;
  }
}

[No input provided. Please paste the Markdown content you wish to have translated.]```sh $ ./android-ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang++ a.cpp -o a -shared -static-libstdc++ && adb push a /data/local/tmp/a

root@kitploit:~
あなたは、オープンソースセキュリティツールのディレクトリであるKitploit向けの専門的なサイバーセキュリティツールコンテンツ翻訳者です。あなたの唯一の役割は、ある言語から別の言語へのMarkdownコンテンツの正確で慣用的かつ簡潔な翻訳を生成することです。

重大なルール:
- 自然言語のテキストのみを翻訳すること。コード、ターミナルコマンド、ファイルパス、URL、パッケージ名、CVE ID、環境変数名、識別子を決して翻訳しないでください。
- すべてのMarkdown構造をそのまま維持すること:見出し、リスト、ネストされたリスト、テーブル(配置を含む)、リンク、画像、インラインコード、コードブロック、引用、HTMLブロック、水平線。
- 正確なフォーマットを維持すること:空白、改行、インデント、テーブルの列数と配置マーカー。
- バッジ、シールド、ステータス画像はそのまま維持すること。
- コードの動作を変える可能性のある句読点を追加、削除、変更しないこと。
- 翻訳されたテキストのみを返すこと。前置きなし、「ここに翻訳があります」なし、「もちろん」なし、説明なし、コメントなし、メタテキストなし。
- 応答をMarkdownコードブロックで絶対に囲まないこと。
- JSON、YAML、XML、配列、オブジェクト、キー/値ラッパー、スキーマ、「translated」「language」「markdown」「content」「result」などのフィールドを絶対に出力しないこと。
- 応答は生のMarkdown/プレーンテキストコンテンツのみであること。ソースにコードブロック内のJSON/YAML/XMLの例が含まれている場合、それらの例をMarkdownの一部としてそのまま維持し、応答全体を構造化オブジェクトに変えないこと。
- 質問をしないこと。会話をしないこと。
- 技術用語を安全に翻訳できない場合は、壊すリスクを冒さずに翻訳せずに残すこと。```js
[device]-> 
function readStdString(str) {
  if ((str.readU8() & 1) === 1) { // size LSB (=1) indicates if it's a long string
    return str.add(2 * Process.pointerSize).readPointer().readUtf8String();    
  }
  return str.add(1).readUtf8String();  
}
[device]-> Module.load('/data/local/tmp/a');
[device]-> var fp_create_stdstr = Module.findExportByName('a', 'create_stdstr');
[device]-> var createStdString = new NativeFunction(fp_create_stdstr, 'pointer', ['pointer', 'int']);
[device]-> var stdstr1 = createStdString(Memory.allocUtf8String("abcd"), 3);
"0x07691234567"
[device]-> readStdString(stdstr1);
"abc"

Cモジュールをロードする

  • https://frida.re/docs/javascript-api/#cmodule
  • https://frida.re/news/2019/09/18/frida-12-7-released/```sh $ ./aarch64-linux-android21-clang /tmp/b.c -o /tmp/a -shared ../sysroot/usr/lib/aarch64-linux-android/21/liblog.so && adb push /tmp/a /data/local/tmp/a
root@kitploit:~
これは166のチャンク13で、長いMarkdown文書を順番に翻訳しています。
ソース言語はenです。
ターゲット言語はjaです。
コンテンツタイプ:README チャンク13/166.

チャンク固有のルール:
1. 自然言語テキストのみを翻訳してください。決して翻訳しないもの:コードブロック、シェルコマンド、ファイルパス、URL、パッケージ名、技術識別子、CVE ID、環境変数名。
2. すべてのMarkdown構文をそのまま正確に保持してください。
3. "## Chunk N"、"## Part N"、"## Continued from..."、"## Translation of chunk..." のような導入見出しを追加しないでください。"End of chunk N"や"Content continues..."のようなマーカーも追加しないでください。
4. 省略を示すために"…"の省略記号を追加しないでください。提供された正確なテキストを構造ごとに文字通り翻訳してください。
5. チャンクの境界は意図的です。チャンクをシームレスに連結できるように構造を保持してください。
6. 翻訳後のテキストのみを返してください。前置き、コメント、コードブロックでのラッピング、JSON/YAML/XML、配列、オブジェクト、スキーマ、キー/バリューラッパーは一切含めないでください。
7. チャンクが段落の途中から始まる場合は、その時点から翻訳を続けてください。ソースに存在しない限り、先頭に改行やインデントを追加しないでください。

入力:```c
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h>    

#define TAG "TEST1"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)

void test(void) {
  FILE* fp = popen("ls -l /sdcard 2>&1", "r");
  if (fp == NULL)
    LOGE("executing cmd failed");
  char b[256];
  while (fgets(b, sizeof(b), fp) != NULL) {
    LOGI("%s", b);
  }
  pclose(fp);
}


ハッキングニュース```sh

$ frida -Uf com.app --no-pause --enable-jit -e "Module.load('/data/local/tmp/a')" [ ] -> new NativeFunction(Module.findExportByName('a', 'test'), 'void', [])()

root@kitploit:~
<br>[⬆ Back to top](#table-of-contents)



#### 一回限りのウォッチポイント

`funcPtr` をインターセプトして、権限を削除することで(`mprotect`を使用)誰が `x2` に読み取り/書き込みをしたかをログに記録します。```js
Process.setExceptionHandler(function(exp) {
  console.warn(JSON.stringify(Object.assign(exp, { _lr: DebugSymbol.fromAddress(exp.context.lr), _pc: DebugSymbol.fromAddress(exp.context.pc) }), null, 2));
  Memory.protect(exp.memory.address, Process.pointerSize, 'rw-');
  // can also use `new NativeFunction(Module.findExportByName(null, 'mprotect'), 'int', ['pointer', 'uint', 'int'])(parseInt(this.context.x2), 2, 0)`
  return true; // goto PC 
});

Interceptor.attach(funcPtr, {
  onEnter: function (args) {
    console.log('onEnter', JSON.stringify({
      x2: this.context.x2,
      mprotect_ret: Memory.protect(this.context.x2, 2, '---'),
      errno: this.errno
    }, null, 2));
  },
  onLeave: function (retval) {
    console.log('onLeave');
  }
});
出力例``` [iOS Device::com.app]-> onEnter { "x2": "0x1c145c6e0", "mprotect_ret": true, "errno": 2 } { "type": "access-violation", "address": "0x1853b0198", "memory": { "operation": "read", "address": "0x1c145c6e0" }, "context": { "lr": "0x100453358", "fp": "0x16fb2e860", "x28": "0x0", "x27": "0x0", "x26": "0x104312600", "x25": "0x0", "x24": "0x0", "x23": "0x0", "x22": "0x0", "x21": "0xb000000422bbda03", "x20": "0x1c4a22560", "x19": "0xb000000422bbda03", "x18": "0x0", "x17": "0x100d25290", "x16": "0x1853b0190", "x15": "0x0", "x14": "0x5", "x13": "0xe5a1c4119597", "x12": "0x10e80ca30", "x11": "0x180000003f", "x10": "0x10e80ca00", "x9": "0x1020ad7c3", "x8": "0x0", "x7": "0x0", "x6": "0x0", "x5": "0x0", "x4": "0xb000000422bbda03", "x3": "0x1c4a22560", "x2": "0x1c145c6e0", "x1": "0x1020ad7c3", "x0": "0x1c145c6e0", "sp": "0x16fb2e790", "pc": "0x1853b0198" }, "nativeContext": "0x16fc42b24" } onLeave ```


⬆ Back to top

ソケットアクティビティ```js

Process .getModuleByName({ linux: 'libc.so', darwin: 'libSystem.B.dylib', windows: 'ws2_32.dll' }[Process.platform]) .enumerateExports().filter(ex => ex.type === 'function' && ['connect', 'recv', 'send', 'read', 'write'].some(prefix => ex.name.indexOf(prefix) === 0)) .forEach(ex => { Interceptor.attach(ex.address, { onEnter: function (args) { var fd = args[0].toInt32(); var socktype = Socket.type(fd); if (socktype !== 'tcp' && socktype !== 'tcp6') return; var address = Socket.peerAddress(fd); if (address === null) return; console.log(fd, ex.name, address.ip + ':' + address.port); } }) })

root@kitploit:~
<details>
<summary>出力例</summary>
	
Android 例```sh
# wrap the script above inside Java.perform
$ frida -Uf com.example.app -l script.js --no-pause
[Android Model-X::com.example.app]-> 117 write 5.0.2.1:5242
117 read 5.0.2.1:5242
135 write 5.0.2.1:4244
135 read 5.0.2.1:4244
135 read 5.0.2.1:4244


⬆ Back to top

Open のインターセプト

特定のファイルが開かれた場合に libc#open をインターセプトし、バックトレースをログに記録する例。```js Interceptor.attach(Module.findExportByName("/system/lib/libc.so", "open"), { onEnter: function(args) { this.flag = false; var filename = Memory.readCString(ptr(args[0])); console.log('filename =', filename) if (filename.endsWith(".xml")) { this.flag = true; var backtrace = Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n\t"); console.log("file name [ " + Memory.readCString(ptr(args[0])) + " ]\nBacktrace:" + backtrace); } }, onLeave: function(retval) { if (this.flag) // passed from onEnter console.warn("\nretval: " + retval); } });

root@kitploit:~
(入力内容が空のため、翻訳するテキストがありません。)```js
var fds = {}; // for f in /proc/`pidof $APP`/fd/*; do echo $f': 'readlink $f; done
Interceptor.attach(Module.findExportByName(null, 'open'), {
  onEnter: function (args) {
    var fname = args[0].readCString();
    if (fname.endsWith('.jar')) {
      this.flag = true;
      this.fname = fname;
    }
  },
  onLeave: function (retval) {
    if (this.flag) {
      fds[retval] = this.fname;
    }
  }
});
['read', 'pread', 'readv'].forEach(fnc => {
  Interceptor.attach(Module.findExportByName(null, fnc), {
    onEnter: function (args) {
      var fd = args[0];
      if (fd in fds)
        console.log(`${fnc}: ${fds[fd]}
	\t${Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n\t')}`);
    }
  });
});
出力例 `com.android.chrome`をインターセプト


⬆ Back to top

シェルコマンドを実行```python

import frida from frida_tools.application import Reactor import threading import click

class Shell(object): def init(self, argv, env): self._stop_requested = threading.Event() self._reactor = Reactor(run_until_return=lambda reactor: self._stop_requested.wait())

root@kitploit:~
    self._device = frida.get_usb_device()
    self._sessions = set()

    self._device.on("child-added", lambda child: self._reactor.schedule(lambda: self._on_child_added(child)))
    self._device.on("child-removed", lambda child: self._reactor.schedule(lambda: self._on_child_removed(child)))
    self._device.on("output", lambda pid, fd, data: self._reactor.schedule(lambda: self._on_output(pid, fd, data)))

    self.argv = argv
    self.env = env
    self.output = []  # stdout will pushed into array

def exec(self):
    self._reactor.schedule(lambda: self._start())
    self._reactor.run()

def _start(self):
    click.secho("✔ spawn(argv={})".format(self.argv), fg='green', dim=True)
    pid = self._device.spawn(self.argv, env=self.env, stdio='pipe')
    self._instrument(pid)

def _stop_if_idle(self):
    if len(self._sessions) == 0:
        self._stop_requested.set()

def _instrument(self, pid):
    click.secho("✔ attach(pid={})".format(pid), fg='green', dim=True)
    session = self._device.attach(pid)
    session.on("detached", lambda reason: self._reactor.schedule(lambda: self._on_detached(pid, session, reason)))
    click.secho("✔ enable_child_gating()", fg='green', dim=True)
    session.enable_child_gating()
    # print("✔ resume(pid={})".format(pid))
    self._device.resume(pid)
    self._sessions.add(session)

def _on_child_added(self, child):
    click.secho("⚡ child_added: {}".format(child), fg='green', dim=True)
    self._instrument(child.pid)

@staticmethod
def _on_child_removed(child):
    click.secho("⚡ child_removed: {}".format(child), fg='green', dim=True)

def _on_output(self, pid, fd, data):
    # print("⚡ output: pid={}, fd={}, data={}".format(pid, fd, repr(data)))
    # fd=0 (input) fd=1(stdout) fd=2(stderr)
    if fd != 2:
        self.output.append(data)

def _on_detached(self, pid, session, reason):
    click.secho("⚡ detached: pid={}, reason='{}'".format(pid, reason), fg='green', dim=True)
    self._sessions.remove(session)
    self._reactor.schedule(self._stop_if_idle, delay=0.5)

@staticmethod
def _on_message(pid, message):
    click.secho("⚡ message: pid={}, payload={}".format(pid, message), fg='green', dim=True)
root@kitploit:~
<details>
<summary>使用例</summary>

ディレクトリの内容を一覧表示:```python
def ls(folder):
    cmd = Shell(['/bin/sh', '-c', 'ls -la ' + folder], None)
    cmd.exec()
    for chunk in cmd.output:
        print(chunk.strip().decode())

iOSからバイナリを取得```python cmd = Shell(['/bin/sh', '-c', 'cat /System/Library/PrivateFrameworks/Example.framework/example'], None) cmd.exec() with open('/tmp/example', 'wb+') as f: f.writelines(cmd.output)

$ file /tmp/example

/tmp/example: Mach-O 64-bit 64-bit architecture=12 executable

root@kitploit:~
</details>

<br>[⬆ トップに戻る](#table-of-contents)


#### モジュール一覧```js
Process.enumerateModulesSync()
    .filter(function(m){ return m['path'].toLowerCase().indexOf('app') !=-1 ; })
    .forEach(function(m) {
        console.log(JSON.stringify(m, null, '  '));
        // to list exports use Module.enumerateExportsSync(m.name)
    });

モジュールとエクスポートの一覧```js sudo frida Process --no-pause --eval 'var x={};Process.enumerateModulesSync().forEach(function(m){x[m.name] = Module.enumerateExportsSync(m.name)});x' -q | less +F

root@kitploit:~
<details>
<summary>出力例</summary>```js
{
  "name": "app_process64",
  "base": "0x6313a1c000",
  "size": 40960,
  "path": "/system/bin/app_process64"
}
{
  "name": "libappfuse.so",
  "base": "0x749ab96000",
  "size": 53248,
  "path": "/system/lib64/libappfuse.so"
}
{
  "name": "[email protected]",
  "base": "0x749b448000",
  "size": 90112,
  "path": "/system/lib64/[email protected]"
}
{
  "name": "[email protected]",
  "base": "0x749ac9e000",
  "size": 94208,
  "path": "/system/lib64/[email protected]"
}
{
  "name": "[email protected]",
  "base": "0x74981e0000",
  "size": 98304,
  "path": "/system/lib64/[email protected]"
}
{
  "name": "[email protected]",
  "base": "0x73fb4cc000",
  "size": 40960,
  "path": "/vendor/lib64/hw/[email protected]"
}
{
  "name": "[email protected]",
  "base": "0x73fb51f000",
  "size": 90112,
  "path": "/system/lib64/vndk-sp-29/[email protected]"
}
{
  "name": "[email protected]",
  "base": "0x73fb542000",
  "size": 94208,
  "path": "/system/lib64/vndk-sp-29/[email protected]"
}
{
  "name": "base.odex",
  "base": "0x73ab4cd000",
  "size": 16965632,
  "path": "/data/app/com.noodlecake.altosadventure-O2YLuwCOq7LbWSkRHkRLcg==/oat/arm64/base.odex"
}
{
  "name": "libfrida-gadget.so",
  "base": "0x73a2c05000",
  "size": 22876160,
  "path": "/data/app/com.noodlecake.altosadventure-O2YLuwCOq7LbWSkRHkRLcg==/lib/arm64/libfrida-gadget.so"
}
{
  "name": "libmain.so",
  "base": "0x73fb894000",
  "size": 73728,
  "path": "/data/app/com.noodlecake.altosadventure-O2YLuwCOq7LbWSkRHkRLcg==/lib/arm64/libmain.so"
}
{
  "name": "libunity.so",
  "base": "0x739bf88000",
  "size": 24461312,
  "path": "/data/app/com.noodlecake.altosadventure-O2YLuwCOq7LbWSkRHkRLcg==/lib/arm64/libunity.so"
}
{
  "name": "libil2cpp.so",
  "base": "0x7396fe6000",
  "size": 25272320,
  "path": "/data/app/com.noodlecake.altosadventure-O2YLuwCOq7LbWSkRHkRLcg==/lib/arm64/libil2cpp.so"
}
{
  "name": "DynamiteLoader.odex",
  "base": "0x73b7ee4000",
  "size": 376832,
  "path": "/data/user_de/0/com.google.android.gms/app_chimera/m/00000278/oat/arm64/DynamiteLoader.odex"
}
{
  "name": "base.odex",
  "base": "0x72f2ee3000",
  "size": 166838272,
  "path": "/data/app/com.google.android.gms-j7RpxBsNAd3ttAYEdp2ahg==/oat/arm64/base.odex"
}
{
  "name": "base.odex",
  "base": "0x7396e6d000",
  "size": 28672,
  "path": "/data/app/com.google.android.trichromelibrary_432418133-X7Kc2Mqi-VXkY12N59kGug==/oat/arm64/base.odex"
}
{
  "name": "base.odex",
  "base": "0x724f15e000",
  "size": 13225984,
  "path": "/data/app/com.google.android.webview-w6i6OBFZ7T_wK4W4TpDAiQ==/oat/arm64/base.odex"
}
{
  "name": "libmonochrome.so",
  "base": "0x73b8592000",
  "size": 76673024,
  "path": "/data/app/com.google.android.webview-w6i6OBFZ7T_wK4W4TpDAiQ==/base.apk!/lib/arm64-v8a/libmonochrome.so"
}
{
  "name": "libnativeNoodleNews.so",
  "base": "0x723add3000",
  "size": 962560,
  "path": "/data/app/com.noodlecake.altosadventure-O2YLuwCOq7LbWSkRHkRLcg==/lib/arm64/libnativeNoodleNews.so"
}
{
  "name": "libconscrypt_gmscore_jni.so",
  "base": "0x7206629000",
  "size": 1130496,
  "path": "/data/app/com.google.android.gms-j7RpxBsNAd3ttAYEdp2ahg==/base.apk!/lib/arm64-v8a/libconscrypt_gmscore_jni.so"
}


⬆ 先頭に戻る

SQLiteクエリのログ記録```js

Interceptor.attach(Module.findExportByName('libsqlite.so', 'sqlite3_prepare16_v2'), { onEnter: function(args) { console.log('DB: ' + Memory.readUtf16String(args[0]) + '\tSQL: ' + Memory.readUtf16String(args[1])); } });

root@kitploit:~
<details>
<summary>出力例</summary>
TODO
</details>

<br>[⬆ トップに戻る](#table-of-contents)

#### system property get```js
Interceptor.attach(Module.findExportByName(null, '__system_property_get'), {
	onEnter: function (args) {
		this._name = args[0].readCString();
		this._value = args[1];
	},
	onLeave: function (retval) {
		console.log(JSON.stringify({
			result_length: retval,
			name: this._name,
			val: this._value.readCString()
		}));
	}
});
出力例```sh {"result_length":"0x0","name":"ro.kernel.android.tracing","val":""} {"result_length":"0x0","name":"ro.config.hw_log","val":""} {"result_length":"0x0","name":"ro.config.hw_module_log","val":""} {"result_length":"0x1","name":"ro.debuggable","val":"0"} {"result_length":"0x1","name":"persist.sys.huawei.debug.on","val":"0"} {"result_length":"0x1","name":"ro.logsystem.usertype","val":"6"} {"result_length":"0x6","name":"ro.board.platform","val":"hi6250"} {"result_length":"0x4","name":"persist.sys.enable_iaware","val":"true"} {"result_length":"0x1","name":"persist.sys.cpuset.enable","val":"1"} {"result_length":"0x4","name":"persist.sys.cpuset.subswitch","val":"1272"} {"result_length":"0x4","name":"persist.sys.boost.durationms","val":"1000"} {"result_length":"0x4","name":"persist.sys.boost.isbigcore","val":"true"} {"result_length":"0x7","name":"persist.sys.boost.freqmin.b","val":"1805000"} {"result_length":"0x4","name":"persist.sys.boost.ipapower","val":"3500"} {"result_length":"0x0","name":"persist.sys.boost.skipframe","val":""} {"result_length":"0x0","name":"persist.sys.boost.byeachfling","val":""} {"result_length":"0x1","name":"debug.force_rtl","val":"0"} {"result_length":"0x0","name":"ro.hardware.gralloc","val":""} {"result_length":"0x6","name":"ro.hardware","val":"hi6250"} {"result_length":"0x0","name":"ro.kernel.qemu","val":""} {"result_length":"0x0","name":"ro.config.hw_force_rotation","val":""} {"result_length":"0x0","name":"persist.fb_auto_alloc","val":""} {"result_length":"0x0","name":"ro.config.hw_lock_res_whitelist","val":""} {"result_length":"0x3","name":"ro.sf.lcd_density","val":"480"} {"result_length":"0x0","name":"persist.sys.dpi","val":""} {"result_length":"0x0","name":"persist.sys.rog.width","val":""} {"result_length":"0x4","name":"dalvik.vm.usejitprofiles","val":"true"} {"result_length":"0x1","name":"debug.atrace.tags.enableflags","val":"0"} {"result_length":"0x1","name":"ro.debuggable","val":"0"} {"result_length":"0x1","name":"debug.force_rtl","val":"0"} {"result_length":"0x0","name":"ro.config.hw_lock_res_whitelist","val":""} .... ```


⬆ トップに戻る

バインダートランザクション```js

var LAST_MSG = ''; Java.perform(() => { Interceptor.attach(Module.findExportByName('libbinder.so', 'ioctl'), { onEnter: function(args) { var binder_write_read_ptr = args[2]; if (args[1] == 0xC0306201) { // BINDER_WRITE_READ var binder_write_read = { // 'fd': args[0].toInt32(), 'write_size': binder_write_read_ptr.readU64(), 'write_consumed': binder_write_read_ptr.add(Process.pointerSize).readU64(), 'write_buffer': binder_write_read_ptr.add(Process.pointerSize * 2).readPointer(), } if (binder_write_read.write_size > 0) { var ptr = binder_write_read.write_buffer.add(binder_write_read.write_consumed + 4); switch (binder_write_read.write_buffer.readU32() & 0xff) { case 0: // BC_TRANSACTION case 1: // BC_REPLY var binder_transaction_data = { 'target': { 'handle': ptr.readU32(), 'ptr': ptr.readPointer() }, 'cookie': ptr.add(8).readPointer(), 'code': ptr.add(16).readU32(), 'flags': ptr.add(20).readU32(), 'sender_pid': ptr.add(24).readS32(), 'sender_euid': ptr.add(28).readU32(), 'data_size': ptr.add(32).readU64(), 'offsets_size': ptr.add(40).readU64(), 'data': { 'ptr': { 'buffer': ptr.add(48).readPointer(), 'offsets': ptr.add(56).readPointer() }, 'buf': ptr.add(48).readByteArray(8) } } var _log = hexdump(binder_transaction_data.data.ptr.buffer, { length: binder_transaction_data.data_size, ansi: true }); if (LAST_MSG.toString() != _log.toString()) { console.log(JSON.stringify(binder_transaction_data, null, 2)); console.log(_log); } break; } } } } }); });

root@kitploit:~
<details>
<summary>出力例</summary>```sh
{
  "target": {
    "handle": 16,
    "ptr": "0x10"
  },
  "cookie": "0x0",
  "code": 22,
  "flags": 16,
  "sender_pid": 0,
  "sender_euid": 0,
  "data_size": "68",
  "offsets_size": "0",
  "data": {
    "ptr": {
      "buffer": "0x78dce3dcf0",
      "offsets": "0x0"
    },
    "buf": {}
  }
}
             0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
78dce3dcf0  04 00 40 01 1d 00 00 00 61 00 6e 00 64 00 72 00  [email protected].
78dce3dd00  6f 00 69 00 64 00 2e 00 6e 00 65 00 74 00 2e 00  o.i.d...n.e.t...
78dce3dd10  77 00 69 00 66 00 69 00 2e 00 49 00 57 00 69 00  w.i.f.i...I.W.i.
78dce3dd20  66 00 69 00 4d 00 61 00 6e 00 61 00 67 00 65 00  f.i.M.a.n.a.g.e.
78dce3dd30  72 00 00 00                                      r...


⬆ Back to top

ネイティブメソッドを明らかにする

registerNativeMethods は、ネイティブ .so ライブラリに対する逆解析防止手法として使用できます。例えば、シンボルを可能な限り隠蔽し、エクスポートされたシンボルを難読化し、最終的に JNI ブリッジに何らかの保護を追加します。 source```js var RevealNativeMethods = function() { var pSize = Process.pointerSize; var env = Java.vm.getEnv(); var RegisterNatives = 215, FindClassIndex = 6; // search "215" @ https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html var jclassAddress2NameMap = {}; function getNativeAddress(idx) { return env.handle.readPointer().add(idx * pSize).readPointer(); } // intercepting FindClass to populate Map<address, jclass> Interceptor.attach(getNativeAddress(FindClassIndex), { onEnter: function(args) { jclassAddress2NameMap[args[0]] = args[1].readCString(); } }); // RegisterNative(jClass*, .., JNINativeMethod methods[nMethods], uint nMethods) // https://android.googlesource.com/platform/libnativehelper/+/master/include_jni/jni.h#977 Interceptor.attach(getNativeAddress(RegisterNatives), { onEnter: function(args) { for (var i = 0, nMethods = parseInt(args[3]); i < nMethods; i++) { / https://android.googlesource.com/platform/libnativehelper/+/master/include_jni/jni.h#129 typedef struct { const char* name; const char* signature; void* fnPtr; } JNINativeMethod; / var structSize = pSize * 3; // = sizeof(JNINativeMethod) var methodsPtr = ptr(args[2]); var signature = methodsPtr.add(i * structSize + pSize).readPointer(); var fnPtr = methodsPtr.add(i * structSize + (pSize * 2)).readPointer(); // void fnPtr var jClass = jclassAddress2NameMap[args[0]].split('/'); var methodName = methodsPtr.add(i * structSize).readPointer().readCString(); console.log('\x1b[3' + '6;01' + 'm', JSON.stringify({ module: DebugSymbol.fromAddress(fnPtr)['moduleName'], // https://www.frida.re/docs/javascript-api/#debugsymbol package: jClass.slice(0, -1).join('.'), class: jClass[jClass.length - 1], method: methodName, // methodsPtr.readPointer().readCString(), // char* name signature: signature.readCString(), // char* signature TODO Java bytecode signature parser { Z: 'boolean', B: 'byte', C: 'char', S: 'short', I: 'int', J: 'long', F: 'float', D: 'double', L: 'fully-qualified-class;', '[': 'array' } https://github.com/skylot/jadx/blob/master/jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java address: fnPtr }), '\x1b[39;49;00m'); } } }); }

Java.perform(RevealNativeMethods);

root@kitploit:~
@OldVersion```js
var fIntercepted = false;

function revealNativeMethods() {
    if (fIntercepted === true) {
        return;
    }
    var jclassAddress2NameMap = {};
    var androidRunTimeSharedLibrary = "libart.so"; // may change between devices
    Module.enumerateSymbolsSync(androidRunTimeSharedLibrary).forEach(function(symbol){
        switch (symbol.name) {
            case "_ZN3art3JNI21RegisterNativeMethodsEP7_JNIEnvP7_jclassPK15JNINativeMethodib":
                /*
                    $ c++filt "_ZN3art3JNI21RegisterNativeMethodsEP7_JNIEnvP7_jclassPK15JNINativeMethodib"
                    art::JNI::RegisterNativeMethods(_JNIEnv*, _jclass*, JNINativeMethod const*, int, bool)
                */
                var RegisterNativeMethodsPtr = symbol.address;
                console.log("RegisterNativeMethods is at " + RegisterNativeMethodsPtr);
                Interceptor.attach(RegisterNativeMethodsPtr, {
                    onEnter: function(args) {
                        var methodsPtr = ptr(args[2]);
                        var methodCount = parseInt(args[3]);
                        for (var i = 0; i < methodCount; i++) {
                            var pSize = Process.pointerSize;
                            /*
                                https://android.googlesource.com/platform/libnativehelper/+/master/include_jni/jni.h#129
                                typedef struct {
                                    const char* name;
                                    const char* signature;
                                    void* fnPtr;
                                } JNINativeMethod;
                            */
                            var structSize = pSize * 3; // JNINativeMethod contains 3 pointers
                            var namePtr = Memory.readPointer(methodsPtr.add(i * structSize));
                            var sigPtr = Memory.readPointer(methodsPtr.add(i * structSize + pSize));
                            var fnPtrPtr = Memory.readPointer(methodsPtr.add(i * structSize + (pSize * 2)));
                            // output schema: className#methodName(arguments)returnVal@address
                            console.log(
                                // package & class, replacing forward slash with dot for convenience
                                jclassAddress2NameMap[args[0]].replace(/\//g, '.') +
                                '#' + Memory.readCString(namePtr) + // method
                                Memory.readCString(sigPtr) + // signature (arguments & return type)
                                '@' + fnPtrPtr // C side address
                            );
                        }
                    },
                    onLeave: function (ignoredReturnValue) {}
                });
                break;
            case "_ZN3art3JNI9FindClassEP7_JNIEnvPKc": // art::JNI::FindClass
                Interceptor.attach(symbol.address, {
                    onEnter: function(args) {
                        if (args[1] != null) {
                            jclassAddress2NameMap[args[0]] = Memory.readCString(args[1]);
                        }
                    },
                    onLeave: function (ignoredReturnValue) {}
                });
                break;
        }
    });
    fIntercepted = true;
}

Java.perform(revealNativeMethods);
出力例```sh $ frida -Uf com.google.android.apps.photos --no-pause -l script.js ``` これは、より長いMarkdown文書の翻訳シーケンスにおける166個中57番目のチャンクです。 ソース言語は英語(en)です。 ターゲット言語:日本語(ja)。 コンテンツタイプ:READMEチャンク57/166。```sh {"class":"org/chromium/net/GURLUtils","method":"nativeGetOrigin","signature":"(Ljava/lang/String;)Ljava/lang/String;","address":"0x..da910"} .. ```


⬆ トップに戻る

メソッド引数のログ記録```python

def on_message(m, _data): if m['type'] == 'send': print(m['payload']) elif m['type'] == 'error': print(m)

def switch(argument_key, idx): """ c/c++ variable type to javascript reader switch implementation # TODO handle other arguments, [long, longlong..] :param argument_key: variable type :param idx: index in symbols array :return: javascript to read the type of variable """ argument_key = argument_key.replace(' ', '') return '%d: %s' % (idx, { 'int': 'args[%d].toInt32(),', 'unsignedint': 'args[%d].toInt32(),', 'std::string': 'Memory.readUtf8String(Memory.readPointer(args[%d])),', 'bool': 'Boolean(args[%d]),' }[argument_key] % idx)

def list_symbols_from_object_files(module_id): import subprocess return subprocess.getoutput('nm --demangle --dynamic %s' % module_id)

def parse_nm_output(nm_stdout, symbols): for line in nm_stdout.splitlines(): split = line.split() open_parenthesis_idx = line.find('(') raw_arguments = [] if open_parenthesis_idx == -1 else line[open_parenthesis_idx + 1:-1] if len(raw_arguments) > 0: # ignore methods without arguments raw_argument_list = raw_arguments.split(',') symbols.append({ 'address': split[0], 'type': split[1], # @see Symbol Type Table 'name': split[2][:split[2].find('(')], # method name 'args': raw_argument_list })

def get_js_script(method, module_id): js_script = """ var moduleName = "{{moduleName}}", nativeFuncAddr = {{methodAddress}}; Interceptor.attach(Module.findExportByName(null, "dlopen"), { onEnter: function(args) { this.lib = Memory.readUtf8String(args[0]); console.log("[] dlopen called with: " + this.lib); }, onLeave: function(retval) { if (this.lib.endsWith(moduleName)) { Interceptor.attach(Module.findBaseAddress(moduleName).add(nativeFuncAddr), { onEnter: function(args) { console.log("[] hook invoked", JSON.stringify({{arguments}}, null, '\t')); } }); } } }); """ replace_map = { '{{moduleName}}': module_id, '{{methodAddress}}': '0x' + method['address'], '{{arguments}}': '{' + ''.join([switch(method['args'][i], i + 1) for i in range(len(method['args']))]) + '}' } for k, v in replace_map.items(): js_script = js_script.replace(k, v) print('[+] JS Script:\n', js_script) return js_script

def main(app_id, module_id, method): """ $ python3.x+ script.py --method SomeClass::someMethod --app com.company.app --module libfoo.so :param app_id: application identifier / bundle id :param module_id: shared object identifier / known suffix, will iterate loaded modules (@see dlopen) :param method: method/symbol name :return: hook native method and print arguments when invoked """ # TODO extract all app's modules via adb shell -c 'ls -lR /data/app/' + app_if + '*' | grep "\.so"

root@kitploit:~
nm_stdout = list_symbols_from_object_files(module_id)

symbols = []
parse_nm_output(nm_stdout, symbols)

selection_idx = None
for idx, symbol in enumerate(symbols):
    if method is None:  # if --method flag is not passed
        print("%4d) %s (%d)" % (idx, symbol['name'], len(symbol['args'])))
    elif method == symbol['name']:
        selection_idx = idx
        break
if selection_idx is None:
    if method is None:
        selection_idx = input("Enter symbol number: ")
    else:
        print('[+] Method not found, remove method flag to get list of methods to select from, `nm` stdout:')
        print(nm_stdout)
        exit(2)

method = symbols[int(selection_idx)]
print('[+] Selected method: %s' % method['name'])
print('[+] Method arguments: %s' % method['args'])

from frida import get_usb_device
device = get_usb_device()
pid = device.spawn([app_id])
session = device.attach(pid)
script = session.create_script(get_js_script(method, module_id))
script.on('message', on_message)
script.load()
device.resume(app_id)
# keep hook alive
from sys import stdin
stdin.read()

if name == 'main': from argparse import ArgumentParser parser = ArgumentParser() parser.add_argument('--app', help='app identifier "com.company.app"') parser.add_argument('--module', help='loaded module name "libfoo.2.so"') parser.add_argument('--method', help='method name "SomeClass::someMethod", if empty it will print select-list') args = parser.parse_args() main(args.app, args.module, args.method)

root@kitploit:~
<details>
<summary>シンボル種別表</summary>
<pre>
    "A" シンボルの値は絶対値であり、以降のリンクでは変更されません。
    "B" シンボルは未初期化データセクション(BSSとして知られる)にあります。
    "C" シンボルはコモンシンボルです。コモンシンボルは未初期化データです。
       リンク時に、同じ名前の共通シンボルが複数現れることがあります。  
       シンボルがどこかで定義されている場合、共通シンボルは未定義参照として扱われます。
    "D" シンボルは初期化データセクションにあります。
    "G" シンボルは小さなオブジェクト用の初期化データセクションにあります。
       一部のオブジェクトファイル形式では、大きなグローバル配列とは対照的に、グローバルint変数のような小さなデータオブジェクトへの
       より効率的なアクセスが許可されています。
    "I" シンボルは別のシンボルへの間接参照です。
       これは、めったに使用されないa.outオブジェクトファイル形式に対するGNU拡張です。
    "N" シンボルはデバッグシンボルです。
    "R" シンボルは読み取り専用データセクションにあります。
    "S" シンボルは小さなオブジェクト用の未初期化データセクションにあります。
    "T" シンボルはテキスト(コード)セクションにあります。
    "U" シンボルは未定義です。
    "V" シンボルは弱オブジェクトです。弱定義シンボルが通常定義シンボルとリンクされると、
        通常定義シンボルがエラーなく使用されます。  
        弱未定義シンボルがリンクされ、シンボルが定義されていない場合、弱シンボルの値は
        エラーなくゼロになります。
    "W" シンボルは、特に弱オブジェクトシンボルとしてタグ付けされていない弱シンボルです。
        弱定義シンボルが通常定義シンボルとリンクされると、
        通常定義シンボルがエラーなく使用されます。  
        弱未定義シンボルがリンクされ、シンボルが定義されていない場合、シンボルの値は
        システム固有の方法でエラーなく決定されます。  
        一部のシステムでは、大文字はデフォルト値が指定されていることを示します。
    "-" シンボルはa.outオブジェクトファイル内のstabsシンボルです。  
        この場合、次に出力される値は、stabs otherフィールド、stabs descフィールド、stabタイプです。  
        Stabsシンボルはデバッグ情報を保持するために使用されます。
    "?" シンボルタイプは不明、またはオブジェクトファイル形式固有です。
</pre>
</details>

<br>[⬆ トップに戻る](#table-of-contents)

#### 読み込まれたクラスの列挙

そして、`pkg.classes` という名前のファイルに保存します```bash
$ frida -U com.pkg -qe 'Java.perform(function(){Java.enumerateLoadedClasses({"onMatch":function(c){console.log(c);}});});' -o pkg.classes
出力例 TODO


⬆ Back to top

クラスの説明

クラスのメソッドとメンバーを取得します。```js Object.getOwnPropertyNames(Java.use('com.company.CustomClass').proto).join('\n\t')

root@kitploit:~
名前の衝突がある場合、メソッドとメンバーが同じ名前の場合、メンバーにアンダースコアが追加されます。 [source](https://github.com/frida/frida-java/pull/21)```js
	let fieldJsName = env.stringFromJni(fieldName);
	while (jsMethods.hasOwnProperty(fieldJsName)) {
		fieldJsName = '_' + fieldJsName;
	}
出力例 TODO


⬆ 先頭に戻る

WiFiをオフにする

最初のアクティビティの作成時にWiFiをオフにします。```js var WifiManager = Java.use("android.net.wifi.WifiManager"); Java.use("android.app.Activity").onCreate.overload("android.os.Bundle").implementation = function(bundle) { var wManager = Java.cast(this.getSystemService("wifi"), WifiManager); console.log('isWifiEnabled ?', wManager.isWifiEnabled()); wManager.setWifiEnabled(false); this.$init(bundle); }

root@kitploit:~
<details>
<summary>出力例</summary>
TODO
</details>

<br>[⬆ 先頭に戻る](#table-of-contents)

#### プロキシの設定

指定されたIPアドレスとポートを使用して、システム全体のプロキシを設定します。```js
var ActivityThread      = Java.use('android.app.ActivityThread');
var ConnectivityManager = Java.use('android.net.ConnectivityManager');
var ProxyInfo           = Java.use('android.net.ProxyInfo');

var proxyInfo           = ProxyInfo.$new('192.168.1.10', 8080, ''); // change to null in order to disable the proxy.
var context = ActivityThread.currentApplication().getApplicationContext();
var connectivityManager = Java.cast(context.getSystemService('connectivity'), ConnectivityManager);
connectivityManager.setGlobalProxy(proxyInfo);
出力例 TODO


⬆ 先頭に戻る

IMEIの取得

また、IMEIのフックと変更も可能です。```js function getIMEI(){ console.log('IMEI =', Java.use("android.telephony.TelephonyManager").$new().getDeviceId()); } Java.perform(getIMEI)

root@kitploit:~
<details>
<summary>出力例</summary>
TODO
</details>

<br>[⬆ トップに戻る](#table-of-contents)

#### io InputStream のフック

`InputputStream` をフックし、バッファを `ascii` として出力、文字数制限と除外リスト付き。```js
function binaryToHexToAscii(array, readLimit) {
    var result = [];
    // read 100 bytes #performance
    readLimit = readLimit || 100;
    for (var i = 0; i < readLimit; ++i) {
        result.push(String.fromCharCode( // hex2ascii part
            parseInt(
                ('0' + (array[i] & 0xFF).toString(16)).slice(-2), // binary2hex part
                16
            )
        ));
    }
    return result.join('');
}

function hookInputStream() {
    Java.use('java.io.InputStream')['read'].overload('[B').implementation = function(b) {
        // execute original and save return value
        var retval = this.read(b);
        var resp = binaryToHexToAscii(b);
        // conditions to not print garbage packets
        var reExcludeList = new RegExp(['Mmm'/*, 'Ping' /*, ' Yo'*/].join('|'));
        if ( ! reExcludeList.test(resp) ) {
            console.log(resp);
        }
        var reIncludeList = new RegExp(['AAA', 'BBB', 'CCC'].join('|')); 
        if ( reIncludeList.test(resp) ) {
            send( binaryToHexToAscii(b, 1200) );
        }
        return retval;
    };
}

Java.perform(hookInputStream);
出力例 TODO


⬆ Back to top

Android Toastの作成```js

// 0 = // https://developer.android.com/reference/android/widget/Toast#LENGTH_LONG Java.scheduleOnMainThread(() => { Java.use("android.widget.Toast") .makeText(Java.use("android.app.ActivityThread").currentApplication().getApplicationContext(), Java.use("java.lang.StringBuilder").$new("Text to Toast here"), 0).show(); });

root@kitploit:~
<details>
<summary>出力例</summary>
TODO
</details>
<br>[⬆ トップに戻る](#table-of-contents)

#### 条件を待機
特定のDLLがUnityアプリにロードされるまで待機し、ホットスワップを実装できます。```javascript
var awaitForCondition = function(callback) {
    var int = setInterval(function() {
        if (Module.findExportByName(null, "mono_get_root_domain")) {
            clearInterval(int);
            callback();
            return;
        }
    }, 0);
}

function hook() {
    Interceptor.attach(Module.findExportByName(null, "mono_assembly_load_from_full"), {
        onEnter: function(args) {
            this._dll = Memory.readUtf8String(ptr(args[1]));
            console.log('[*]', this._dll);
        },
        onLeave: function(retval) {          
            if (this._dll.endsWith("Assembly-CSharp.dll")) {
                console.log(JSON.stringify({
                    retval: retval,
                    name: this._dll
                }, null, 2));
            }
        }
    });
}
Java.perform(awaitForCondition(hook));
出力例 TODO


⬆ 先頭に戻る

WebView URL の切り替え

WebView が URL を切り替えるたびにログを記録します。```js Java.use("android.webkit.WebView").loadUrl.overload("java.lang.String").implementation = function (s) { send(s.toString()); this.loadUrl.overload("java.lang.String").call(this, s); };

root@kitploit:~
<details>
<summary>出力例</summary>
TODO
</details>

<br>[⬆ Back to top](#table-of-contents)

#### 実行時文字列の表示

StringBuilder/Bufferの`toString`をフックし、スタックトレースを出力します。```js
Java.perform(function() {
  ['java.lang.StringBuilder', 'java.lang.StringBuffer'].forEach(function(clazz, i) {
    console.log('[?] ' + i + ' = ' + clazz);
    var func = 'toString';
    Java.use(clazz)[func].implementation = function() {
      var ret = this[func]();
      if (ret.indexOf('') != -1) {
        // print stacktrace if return value contains specific string
        Java.perform(function() {
          var jAndroidLog = Java.use("android.util.Log"), jException = Java.use("java.lang.Exception");
          console.log( jAndroidLog.getStackTraceString( jException.$new() ) );
        }); 
      }   
      send('[' + i + '] ' + ret);
      return ret;
    }   
  }); 
});
出力例 TODO


⬆ トップに戻る

共有プリファレンスの更新を出力```js

Java.perform(function() { var shared_pref_class = Java.use('android.app.SharedPreferencesImpl$EditorImpl');

root@kitploit:~
shared_pref_class.putString.overload('java.lang.String', 'java.lang.String').implementation = function(k, v) {
    console.log('Shared preference updated: ', k, '=', v);
    return this.putString(k, v);
}

shared_pref_class.putInt.overload('java.lang.String', 'int').implementation = function(k, v) {
    console.log('Shared preference updated: ', k, '=', v);
    return this.putInt(k, v);
}


shared_pref_class.putFloat.overload('java.lang.String', 'float').implementation = function(k, v) {
    console.log('Shared preference updated: ', k, '=', v);
    return this.putFloat(k, v);
}

shared_pref_class.putBoolean.overload('java.lang.String', 'boolean').implementation = function(k, v) {
    console.log('Shared preference updated: ', k, '=', v);
    return this.putBoolean(k, v);
}

shared_pref_class.putLong.overload('java.lang.String', 'long').implementation = function(k, v) {
    console.log('Shared preference updated: ', k, '=', v);
    return this.putLong(k, v);
}

shared_pref_class.putStringSet.overload('java.lang.String', java.util.Set).implementation = function(k, v) {
    console.log('Shared preference updated: ', k, '=', v);
    return this.putStringSet(k, v);
}

});

root@kitploit:~
<details>
<summary>出力例</summary>
TODO
</details>

<br>[⬆ 先頭に戻る](#table-of-contents)




#### 文字列比較```js
Java.perform(function() {
    var str = Java.use('java.lang.String'), objectClass = 'java.lang.Object';
    str.equals.overload(objectClass).implementation = function(obj) {
        var response = str.equals.overload(objectClass).call(this, obj);
        if (obj) {
            if (obj.toString().length > 5) {
                send(str.toString.call(this) + ' == ' + obj.toString() + ' ? ' + response);
            }
        }
        return response;
    }
});
出力例 TODO


⬆ トップに戻る

アドレスによるJNIフック

モジュール名とメソッドアドレスでネイティブメソッドをフックし、引数を出力します。```js var moduleName = "libfoo.so"; var nativeFuncAddr = 0x1234; // $ nm --demangle --dynamic libfoo.so | grep "Class::method("

Interceptor.attach(Module.findExportByName(null, "dlopen"), { onEnter: function(args) { this.lib = Memory.readUtf8String(args[0]); console.log("dlopen called with: " + this.lib); }, onLeave: function(retval) { if (this.lib.endsWith(moduleName)) { console.log("ret: " + retval); var baseAddr = Module.findBaseAddress(moduleName); Interceptor.attach(baseAddr.add(nativeFuncAddr), { onEnter: function(args) { console.log("[-] hook invoked"); console.log(JSON.stringify({ a1: args[1].toInt32(), a2: Memory.readUtf8String(Memory.readPointer(args[2])), a3: Boolean(args[3]) }, null, '\t')); } }); } } });

root@kitploit:~
<details>
<summary>出力例</summary>
TODO
</details>

<br>[⬆ トップに戻る](#table-of-contents)

#### フックコンストラクタ```js
Java.use('java.lang.StringBuilder').$init.overload('java.lang.String').implementation = function(stringArgument) {
      console.log("c'tor");
      return this.$init(stringArgument);
};
出力例 TODO


⬆ トップに戻る

フックリフレクション

`java.lang.reflect.Method#invoke(Object obj, Object... args, boolean bool)````javascript Java.use('java.lang.reflect.Method').invoke.overload('java.lang.Object', '[Ljava.lang.Object;', 'boolean').implementation = function(a,b,c) { console.log('hooked!', a, b, c); return this.invoke(a,b,c); };

root@kitploit:~
<details>
<summary>出力例</summary>
TODO
</details>

<br>[⬆ トップに戻る](#table-of-contents)

#### Trace クラス

クラスメソッドのトレース。カラフルな表示と、JSON およびスタックトレースとして出力するオプション。

TODO: コンストラクタのトレースを追加。```js

var Color = {
    RESET: "\x1b[39;49;00m", Black: "0;01", Blue: "4;01", Cyan: "6;01", Gray: "7;11", Green: "2;01", Purple: "5;01", Red: "1;01", Yellow: "3;01",
    Light: {
        Black: "0;11", Blue: "4;11", Cyan: "6;11", Gray: "7;01", Green: "2;11", Purple: "5;11", Red: "1;11", Yellow: "3;11"
    }
};

/**
 *
 * @param input. 
 *      If an object is passed it will print as json 
 * @param kwargs  options map {
 *     -l level: string;   log/warn/error
 *     -i indent: boolean;     print JSON prettify
 *     -c color: @see ColorMap
 * }
 */
var LOG = function (input, kwargs) {
    kwargs = kwargs || {};
    var logLevel = kwargs['l'] || 'log', colorPrefix = '\x1b[3', colorSuffix = 'm';
    if (typeof input === 'object')
        input = JSON.stringify(input, null, kwargs['i'] ? 2 : null);
    if (kwargs['c'])
        input = colorPrefix + kwargs['c'] + colorSuffix + input + Color.RESET;
    console[logLevel](https://github.com/iddoeldor/frida-snippets/blob/master/input);
};

var printBacktrace = function () {
    Java.perform(function() {
        var android_util_Log = Java.use('android.util.Log'), java_lang_Exception = Java.use('java.lang.Exception');
        // getting stacktrace by throwing an exception
        LOG(android_util_Log.getStackTraceString(java_lang_Exception.$new()), { c: Color.Gray });
    });
};

function traceClass(targetClass) {
    var hook;
    try {
        hook = Java.use(targetClass);
    } catch (e) {
        console.error("trace class failed", e);
        return;
    }

    var methods = hook.class.getDeclaredMethods();
    hook.$dispose();

    var parsedMethods = [];
    methods.forEach(function (method) {
        var methodStr = method.toString();
        var methodReplace = methodStr.replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1];
         parsedMethods.push(methodReplace);
    });

    uniqBy(parsedMethods, JSON.stringify).forEach(function (targetMethod) {
        traceMethod(targetClass + '.' + targetMethod);
    });
}


function traceMethod(targetClassMethod) {
    try {
        var delim = targetClassMethod.lastIndexOf('.');
        if (delim === -1)
            return;

        var targetClass = targetClassMethod.slice(0, delim);
        var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length);

        var hook = Java.use(targetClass);
        var overloadCount = hook[targetMethod].overloads.length;

        LOG({ tracing: targetClassMethod, overloaded: overloadCount }, { c: Color.Green });

        for (var i = 0; i < overloadCount; i++) {
            hook[targetMethod].overloads[i].implementation = function () {
                var log = { '#': targetClassMethod, args: [] };

                for (var j = 0; j < arguments.length; j++) {
                    var arg = arguments[j];
                    // quick&dirty fix for java.io.StringWriter char[].toString() impl because frida prints [object Object]
                    if (j === 0 && arguments[j]) {
                        if (arguments[j].toString() === '[object Object]') {
                            var s = [];
                            for (var k = 0, l = arguments[j].length; k < l; k++) {
                                s.push(arguments[j][k]);
                            }
                            arg = s.join('');
                        }
                    }
                    log.args.push({ i: j, o: arg, s: arg ? arg.toString(): 'null'});
                }

                var retval;
                try {
                    retval = this[targetMethod].apply(this, arguments); // might crash (Frida bug?)
                    log.returns = { val: retval, str: retval ? retval.toString() : null };
                } catch (e) {
                    console.error(e);
                }
                LOG(log, { c: Color.Blue });
                return retval;
            }
        }
    } catch(error) {
        LOG({ tracing: targetClassMethod, "overloaded": 0}, { c: Color.Red });
    }
}
// remove duplicates from array
function uniqBy(array, key) {
    var seen = {};
    return array.filter(function (item) {
        var k = key(item);
        return seen.hasOwnProperty(k) ? false : (seen[k] = true);
    });
}


var Main = function() {
    Java.perform(function () { // avoid java.lang.ClassNotFoundException
        [
            // "java.io.File",
            'java.net.Socket'
        ].forEach(traceClass);

        Java.use('java.net.Socket').isConnected.overload().implementation = function () {
            LOG('Socket.isConnected.overload', { c: Color.Light.Cyan });
            printBacktrace();
            return true;
        }
    });
};

Java.perform(Main);


出力例 TODO


⬆ Back to top

Android IDの取得

Androidでは、ANDROID_IDはアプリケーションごとに一意です。```javascript function getContext() { return Java.use('android.app.ActivityThread').currentApplication().getApplicationContext().getContentResolver(); }

function logAndroidId() { console.log('[-]', Java.use('android.provider.Settings$Secure').getString(getContext(), 'android_id')); }

root@kitploit:~
<details>
<summary>出力例</summary>
https://stackoverflow.com/a/54818023/2655092
</details>

<br>[⬆ 先頭に戻る](#table-of-contents)

#### 場所の変更```js
Java.perform(() => {
	var Location = Java.use('android.location.Location');
	Location.getLatitude.implementation = function() {
		return LATITUDE;
	}
	Location.getLongitude.implementation = function() {
		return LONGITUDE;
	}
})
出力例 TODO


⬆ Back to top

バイパス FLAG_SECURE

スクリーンショット防止のバイパス stackoverflow question```javascript Java.perform(function() { Java.use('android.view.SurfaceView').setSecure.overload('boolean').implementation = function(flag){ console.log('[1] flag:', flag); this.call(false); }; var LayoutParams = Java.use('android.view.WindowManager$LayoutParams'); Java.use('android.view.ViewWindow').setFlags.overload('int', 'int').implementation = function(flags, mask){ console.log('flag secure: ', LayoutParams.FLAG_SECURE.value); console.log('before:', flags); flags = (flags.value & ~LayoutParams.FLAG_SECURE.value); console.log('after:', flags); this.call(this, flags, mask); }; });

root@kitploit:~
<details>
<summary>出力例</summary>
https://stackoverflow.com/a/54818023/2655092
</details>

<br>[⬆ トップに戻る](#table-of-contents)

#### Shared Preferences の更新```javascript
function notifyNewSharedPreference() {
  Java.use('android.app.SharedPreferencesImpl$EditorImpl').putString.overload('java.lang.String', 'java.lang.String').implementation = function(k, v) {
    console.log('[SharedPreferencesImpl]', k, '=', v);
    return this.putString(k, v);
  }
}
出力例 TODO


⬆ 先頭に戻る

Hook overloads```javascript

function hookOverloads(className, func) { var clazz = Java.use(className); var overloads = clazz[func].overloads; for (var i in overloads) { if (overloads[i].hasOwnProperty('argumentTypes') || overloads[i]['argumentTypes'] != undefined) { var parameters = [];

root@kitploit:~
  var curArgumentTypes = overloads[i].argumentTypes, args = [], argLog = '[';
  for (var j in curArgumentTypes) {
    var cName = curArgumentTypes[j].className;
    parameters.push(cName);
    argLog += "'(" + cName + ") ' + v" + j + ",";
    args.push('v' + j);
  }
  argLog += ']';

  var script = "var ret = this." + func + '(' + args.join(',') + ") || '';\n"
    + "console.log(JSON.stringify(" + argLog + "));\n"
    + "return ret;"

  args.push(script);
  clazz[func].overload.apply(this, parameters).implementation = Function.apply(null, args);
}

} }

Java.perform(function() { hookOverloads('java.lang.StringBuilder', '$init'); })

root@kitploit:~
<details>
<summary>出力例</summary>
TODO
</details>

<br>[⬆ 先頭に戻る](#table-of-contents)

#### ブロードキャストレシーバの登録```javascript
Java.perform(() => {
    const MyBroadcastReceiver = Java.registerClass({
        name: 'MyBroadcastReceiver',
        superClass: Java.use('android.content.BroadcastReceiver'),
        methods: {
            onReceive: [{
                returnType: 'void',
                argumentTypes: ['android.content.Context', 'android.content.Intent'],
                implementation: function(context, intent) {
                    // ..
                }
            }]
        },
    });
    let ctx = Java.use('android.app.ActivityThread').currentApplication().getApplicationContext();
    ctx.registerReceiver(MyBroadcastReceiver.$new(), Java.use('android.content.IntentFilter').$new('com.example.JAVA_TO_AGENT'));
});
出力例 TODO


⬆ トップに戻る

インターフェースを実装するクラスの一覧```js

function listClassesImplementsInterface(aInterface) { let classLoaders = Java.enumerateClassLoadersSync() Java.enumerateLoadedClassesSync().forEach(className => { for (let i = 0; i < classLoaders.length; i++) { let classLoader = classLoaders[i] Java.classFactory.loader = classLoader try { let jclass = Java.use(className).class let ifaces = jclass.getInterfaces().toString() jclass = null if (ifaces.indexOf(aInterface) != -1) { console.log(JSON.stringify({ name: className, loader: classLoader.toString(), interfaces: ifaces })) break // we found one ClassLoader, that's enough } } catch (e) { // continue to next ClassLoader } } }) }

root@kitploit:~
<br>[⬆ トップに戻る](#table-of-contents)


#### ステップ数を増やす```js
Java.perform(() => {
  var customSensorEventListener = null;
  var curSteps = 0;
  var totalNumberOfRequiredSteps = 10000;

  function incSteps() {
    Java.perform(() => {
      var sEvent = Java.use('android.hardware.SensorEvent').$new(1);
      sEvent.values.values = Java.array('float', [curSteps]); // https://developer.android.com/reference/android/hardware/SensorEvent#values
      sEvent.timestamp = Java.use('java.lang.Long').$new(Java.use('java.lang.System').nanoTime());
      sEvent.accuracy = Java.use('java.lang.Integer').$new(3); // https://developer.android.com/reference/android/hardware/SensorManager#SENSOR_STATUS_ACCURACY_HIGH
      customSensorEventListener.onSensorChanged(sEvent);
      
      if (curSteps < totalNumberOfRequiredSteps) {
        setTimeout(() => {
          curSteps += 50;
          incSteps();
        }, 1500)
      }
    });
  }

  Java.choose('.CustomSensorEventListener', {  // class that implements SensorEventListener
    onMatch: function (instance) {
      customSensorEventListener = instance;
    },
    onComplete: function () {
      incSteps();
    }
  });
});
出力例 TODO


⬆ トップに戻る

OS ログ```js

var m = 'libsystem_trace.dylib'; // bool os_log_type_enabled(os_log_t oslog, os_log_type_t type); var isEnabledFunc = Module.findExportByName(m, 'os_log_type_enabled'); // _os_log_impl(void *dso, os_log_t log, os_log_type_t type, const char *format, uint8_t *buf, unsigned int size); var logFunc = Module.findExportByName(m, '_os_log_impl');

// Enable all logs Interceptor.attach(isEnabledFunc, { onLeave: function (ret) { ret.replace(0x1); } });

Interceptor.attach(logFunc, { onEnter: function (a) { /* OS_ENUM(os_log_type, uint8_t, OS_LOG_TYPE_DEFAULT = 0x00, OS_LOG_TYPE_INFO = 0x01, OS_LOG_TYPE_DEBUG = 0x02, OS_LOG_TYPE_ERROR = 0x10, OS_LOG_TYPE_FAULT = 0x11); */ var type = a[2]; var format = a[3]; if (type !== 0x2) { console.log(JSON.stringify({ type: type, format: format.readCString(), //buf: a[4].readPointer().readCString() // TODO }, null, 2)); } } })

root@kitploit:~
<details>
<summary>出力例</summary>
TODO
</details>

<br>[⬆ 先頭に戻る](#table-of-contents)



#### iOS アラートボックス```js
var UIAlertController = ObjC.classes.UIAlertController;
var UIAlertAction = ObjC.classes.UIAlertAction;
var UIApplication = ObjC.classes.UIApplication;
var handler = new ObjC.Block({ retType: 'void', argTypes: ['object'], implementation: function () {} });

ObjC.schedule(ObjC.mainQueue, function () {
  var alert = UIAlertController.alertControllerWithTitle_message_preferredStyle_('Frida', 'Hello from Frida', 1);
  var defaultAction = UIAlertAction.actionWithTitle_style_handler_('OK', 0, handler);
  alert.addAction_(defaultAction);
  // Instead of using `ObjC.choose()` and looking for UIViewController instances on the heap, we have direct access through UIApplication:
  UIApplication.sharedApplication().keyWindow().rootViewController().presentViewController_animated_completion_(alert, true, NULL);
})
出力例 TODO


⬆ Back to top

ファイルアクセス

各ファイルオープンを記録```js Interceptor.attach(ObjC.classes.NSFileManager['- fileExistsAtPath:'].implementation, { onEnter: function (args) { console.log('open' , ObjC.Object(args[2]).toString()); } });

root@kitploit:~
<details>
<summary>出力例</summary>
TODO
</details>

<br>[⬆ トップに戻る](#table-of-contents)

#### Observe class```js
function observeClass(name) {
    var k = ObjC.classes[name];
    k.$ownMethods.forEach(function(m) {
        var impl = k[m].implementation;
        console.log('Observing ' + name + ' ' + m);
        Interceptor.attach(impl, {
            onEnter: function(a) {
                this.log = [];
                this.log.push('(' + a[0] + ',' + Memory.readUtf8String(a[1]) + ') ' + name + ' ' + m);
                if (m.indexOf(':') !== -1) {
                    var params = m.split(':');
                    params[0] = params[0].split(' ')[1];
                    for (var i = 0; i < params.length - 1; i++) {
                        try {
                            this.log.push(params[i] + ': ' + new ObjC.Object(a[2 + i]).toString());
                        } catch (e) {
                            this.log.push(params[i] + ': ' + a[2 + i].toString());
                        }
                    }
                }

                this.log.push(
                    Thread.backtrace(this.context, Backtracer.ACCURATE)
                        .map(DebugSymbol.fromAddress)
                        .join('\n')
                );
            },

            onLeave: function(r) {
                try {
                    this.log.push('RET: ' + new ObjC.Object(r).toString());
                } catch (e) {
                    this.log.push('RET: ' + r.toString());
                }

                console.log(this.log.join('\n') + '\n');
            }
        });
    });
}
出力例

`observeClass('Someclass$innerClass');```` Observing Someclass$innerClass - func Observing Someclass$innerClass - empty (0x174670040,parameterName) Someclass$innerClass - func 0x10048dd6c libfoo!0x3bdd6c 0x1005a5dd0 libfoo!0x4d5dd0 0x1832151c0 libdispatch.dylib!_dispatch_client_callout 0x183215fb4 libdispatch.dylib!dispatch_once_f RET: 0xabcdef

root@kitploit:~
</details>

<br>[⬆ トップに戻る](#table-of-contents)


#### iOSアプリケーションのUUIDを検索

各アプリコンテナ内のplistファイルを読み取ることで、アプリにアタッチされた特定のパスのUUIDを取得します。```js
var PLACEHOLDER = '{UUID}';
function extractUUIDfromPath(path) {
    var bundleIdentifier = String(ObjC.classes.NSBundle.mainBundle().objectForInfoDictionaryKey_('CFBundleIdentifier'));
    var path_prefix = path.substr(0, path.indexOf(PLACEHOLDER));
    var plist_metadata = '/.com.apple.mobile_container_manager.metadata.plist';
    var errorPtr = Memory.alloc(Process.pointerSize); 
    Memory.writePointer(errorPtr, NULL); 
    var folders = ObjC.classes.NSFileManager.defaultManager().contentsOfDirectoryAtPath_error_(path_prefix, errorPtr);
    var error = Memory.readPointer(errorPtr);
    if (errorPtr) 
    	console.error( new ObjC.Object( error ) );
    for (var i = 0, l = folders.count(); i < l; i++) {
        var uuid = folders.objectAtIndex_(i);
        var metadata = path_prefix + uuid + plist_metadata;
        var dict = ObjC.classes.NSMutableDictionary.alloc().initWithContentsOfFile_(metadata);
        var enumerator = dict.keyEnumerator();
        var key;
        while ((key = enumerator.nextObject()) !== null) {
            if (key == 'MCMMetadataIdentifier') {
                var appId = String(dict.objectForKey_(key));
                if (appId.indexOf(bundleIdentifier) != -1) {
                    return path.replace(PLACEHOLDER, uuid);
                }
            }
        }
    }
}
console.log( extractUUIDfromPath('/var/mobile/Containers/Data/Application/' + PLACEHOLDER + '/Documents') );


⬆ トップに戻る

クラスメンバーの説明

各クラスインスタンスのメンバー(値付き)のマップを表示する```js ObjC.choose(ObjC.classes[clazz], { onMatch: function (obj) { console.log('onMatch: ', obj); Object.keys(obj.$ivars).forEach(function(v) { console.log('\t', v, '=', obj.$ivars[v]); }); }, onComplete: function () { console.log('onComplete', arguments.length); } });

root@kitploit:~
<details>
<summary>出力例</summary>
TODO	
</details>

<br>[⬆ トップに戻る](#table-of-contents)

#### クラス階層

Object.keys(ObjC.classes) は利用可能なすべてのObjective-Cクラスをリストしますが、実際には現在のプロセスにロードされているすべてのクラス(システムフレームワークを含む)を返します。weak_classdump のように、実行可能ファイル自体からのみクラスをリストしたい場合、Objective-Cランタイムはすでにそのような関数 [objc_copyClassNamesForImage](#https://developer.apple.com/documentation/objectivec/1418485-objc_copyclassnamesforimage?language=objc) を提供しています。```js
var objc_copyClassNamesForImage = new NativeFunction(
    Module.findExportByName(null, 'objc_copyClassNamesForImage'),
    'pointer',
    ['pointer', 'pointer']
);
var free = new NativeFunction(Module.findExportByName(null, 'free'), 'void', ['pointer']);
var classes = new Array(count);
var p = Memory.alloc(Process.pointerSize);

Memory.writeUInt(p, 0);

var path = ObjC.classes.NSBundle.mainBundle().executablePath().UTF8String();
var pPath = Memory.allocUtf8String(path);
var pClasses = objc_copyClassNamesForImage(pPath, p);
var count = Memory.readUInt(p);
for (var i = 0; i < count; i++) {
    var pClassName = Memory.readPointer(pClasses.add(i * Process.pointerSize));
    classes[i] = Memory.readUtf8String(pClassName);
}

free(pClasses);

var tree = {};
classes.forEach(function(name) {
    var clazz = ObjC.classes[name];
    var chain = [name];
    while (clazz = clazz.$superClass) {
        chain.unshift(clazz.$className);
    }

    var node = tree;
    chain.forEach(function(clazz) {
        node[clazz] = node[clazz] || {};
        node = node[clazz];
    });
});

send(tree);
出力例 TODO


⬆ トップに戻る

フックのリフレクション

objc_msgSend をフックする```py import frida, sys

f = open('/tmp/log', 'w')

def on_message(msg, _data): f.write(msg['payload']+'\n')

frida_script = """ Interceptor.attach(Module.findExportByName('/usr/lib/libobjc.A.dylib', 'objc_msgSend'), { onEnter: function(args) { var m = Memory.readCString(args[1]); if (m != 'length' && !m.startsWith('_fastC')) send(m); } }); """ device = frida.get_usb_device() pid = device.spawn(["com.example"]) # or .get_frontmost_application() session = device.attach(pid) script = session.create_script(frida_script) script.on('message', on_message) script.load() device.resume(pid) sys.stdin.read()

root@kitploit:~
```sh
$ sort /tmp/log | uniq -c | sort -n
出力例 TODO


⬆ トップに戻る

モジュール全体のインターセプト

UI関連機能を削減するために、次の手順を使用します:

  1. ログをファイルに出力するために -o /tmp/log1 を使用します
  2. MRUをexcludesListにコピーするために $ sort /tmp/log1 | uniq -c | sort -rn | head -n20 | cut -d# -f2 | paste -sd "," - を使用します```js var mName = 'MyModule', excludeList = ['Alot', 'Of', 'UI', 'Related', 'Functions']; Module.enumerateExportsSync(mName) .filter(function(e) { var fromTypeFunction = e.type == 'function';· var notInExcludes = excludeList.indexOf(e.name) == -1; return fromTypeFunction && notInExcludes; }) .forEach(function(e) { Interceptor.attach(Module.findExportByName(mName, e.name), { onEnter: function(args) { console.log(mName + "#'" + e.name + "'"); } }) })
root@kitploit:~
<details>
<summary>出力例</summary>
TODO	
</details>

<br>[⬆ トップに戻る](#table-of-contents)



#### メモリセグメントのダンプ```js
Process.enumerateRanges('rw-', {
	onMatch: function (range) {
		var fname = `/sdcard/${range.base}_dump`;
		var f = new File(fname, 'wb');
		f.write(instance.base.readByteArray(instance.size));
		f.flush();
		f.close();
		console.log(`base=${range.base} size=${range.size} prot=${range.protection} fname=${fname}`);
	},
	onComplete: function () {}
});
出力例 TODO


⬆ トップに戻る

メモリスキャン```js

function memscan(str) { Process.enumerateModulesSync().filter(m => m.path.startsWith('/data')).forEach(m => { var pattern = str.split('').map(letter => letter.charCodeAt(0).toString(16)).join(' '); try { var res = Memory.scanSync(m.base, m.size, pattern); if (res.length > 0) console.log(JSON.stringify({m, res})); } catch (e) { console.warn(e); } }); }

root@kitploit:~
入力:```js
var memscn = function (str) {
	Process.enumerateModulesSync().forEach(function (m) {
		var pattern = str.split('').map(function (l) { return l.charCodeAt(0).toString(16) }).join(' ');
		try {
			var res = Memory.scanSync(m.base, m.size, pattern);
			if (res.length > 0)
				console.log(JSON.stringify({m, res}, null , 2));
		} catch (e) {
			console.warn(e);
		}
	});
}
出力例 pattern [ 52 41 4e 44 4f 4d ] { "name": "Test", "base": "0x1048fc000", "size": 147000, "path": "/var/containers/Bundle/Application/CD74EB00-9D90-4600-BF5D-F6E5E0CDF878/Test.app/Test" } [{"address":"0x10491f211","size":6}]


⬆ トップに戻る

Stalker```js

var _module = Process.findModuleByName('myModule'); var base = ptr(_module.base); var startTraceOffset = 0xabcd1234, numInstructionsToTrace = 50; var startTrace = base.add(startTraceOffset), endTrace = startTrace.add(4 * (numInstructionsToTrace - 1));

Interceptor.attach(ObjC.classes.CustomClass['- func'].implementation, { onEnter: function (args) { var tid = Process.getCurrentThreadId(); this.tid = tid; console.warn(onEnter [ ${tid} ]);
Stalker.follow(tid, { transform: function (iterator) { var instruction; while ((instruction = iterator.next()) !== null) { // condition to putCallout if (instruction.address <= endTrace && instruction.address >= startTrace) {
// print instruction & registers values iterator.putCallout(function(context) { var offset = ptr(context.pc).sub(base); var inst = Instruction.parse(context.pc).toString(); var modified_inst = inst; inst.replace(/,/g, '').split(' ').forEach(op => { if (op.startsWith('x')) modified_inst = modified_inst.replace(op, context[op]); else if (op.startsWith('w')) modified_inst = modified_inst.replace(op, context[op.replace('w', 'x')]); }); modified_inst = '\x1b[35;01m' + modified_inst + '\x1b[0m'; console.log(x8=${context.x8} x25=${context.x25} x0=${context.x0} x21=${context.x21}) console.log(${offset} ${inst} # ${modified_inst}); }); } iterator.keep(); } } }) },
onLeave: function (retval) { console.log(onLeave [ ${this.tid} ]); // cleanup Stalker.unfollow(this.tid); Stalker.garbageCollect(); }
})

root@kitploit:~
<details>
<summary>出力例</summary>
mul x5, x2, x21 # mul 0x3, 0x4, 0x5
</details>

<br>[⬆ トップに戻る](#table-of-contents)






#### Cpp demangler```sh
$ npm i frida-compile demangler-js -g

スクリプトに追加```js const demangle = require('demangler-js').demangle; ... Module.enumerateExportsSync('library.so') .filter(x => x.name.startsWith('_Z')) .forEach(x => { Interceptor.attach(x.address, { onEnter: function (args) { console.log('[-] ' + demangle(x.name)); } }); });

root@kitploit:~
コンパイル```sh
$ frida-compile script.js -o out.js

実行```sh $ frida -Uf com.app -l out.js

root@kitploit:~
<details>
<summary>出力例</summary>
TODO
</details>

<br>[⬆ トップに戻る](#table-of-contents)


#### アーリーフック

DT_INIT_ARRAY の前にフックを設定する([ソース](https://cs.android.com/android/platform/superproject/+/master:bionic/linker/linker_soinfo.cpp;l=386;drc=android-8.0.0_r1?q=call_constructor&ss=android%2Fplatform%2Fsuperproject))```js
let base;
let do_dlopen = null;
let call_ctor = null;
const target_lib_name = 'targetlib.so';

Process.findModuleByName('linker64').enumerateSymbols().forEach(sym => {
    if (sym.name.indexOf('do_dlopen') >= 0) {
        do_dlopen = sym.address;
    } else if (sym.name.indexOf('call_constructor') >= 0) {
        call_ctor = sym.address;
    }
})

Interceptor.attach(do_dlopen, function (args) {
    if (args[0].readUtf8String().indexOf(target_lib_name) >= 0) {
        Interceptor.attach(call_ctor, function () {
            const module = Process.findModuleByName(target_lib_name);
            base = module.base;
            console.log('loading', target_lib_name, '- base @', base);
	    
	    // DoStuff
        })
    }
})

クレジット:iGio90

出力例 TODO


⬆ トップに戻る

デバイスのプロパティ

iOSデバイスのプロパティを迅速かつ簡易的に抽出する例```js var UIDevice = ObjC.classes.UIDevice.currentDevice(); UIDevice.$ownMethods .filter(function(method) { return method.indexOf(':') == -1 /* filter out methods with parameters / && method.indexOf('+') == -1 / filter out public methods */ }) .forEach(function(method) { console.log(method, ':', UIDevicemethod) }) console.log('executablePath =', ObjC.classes.NSBundle.mainBundle().executablePath().toString());

root@kitploit:~
それの能力を低下させる```js
if (ObjC.available) { 
	var processInfo = ObjC.classes.NSProcessInfo.processInfo();
	var versionString = processInfo.operatingSystemVersionString().toString();
	// E.g. "Version 13.5 (Build 17F75)"
	var ver = versionString.split(' ');
	var version = ver[1];
	// E.g. 13.5
	console.log("iOS version: " + version);  
}
出力例``` - adjTrackingEnabled : true - adjFbAttributionId : - adjVendorId : 4AAAAAAA-CECC-4BBB-BDDD-DEEEEEEEED18 - adjDeviceType : iPhone - adjDeviceName : iPhone8,2 - adjCreateUuid : dfaaaa2-ebbd-4ccc-addd-eaeeeeeeee7c - adjIdForAdvertisers : 7AAAAA3A-4BBB-4CCC-BDDD-0EEEEEEEE8A6 - sbf_bannerGraphicsQuality : 100 - sbf_controlCenterGraphicsQuality : 100 - sbf_homeScreenFolderGraphicsQuality : 100 - sbf_searchTransitionGraphicsQuality : 100 - sbf_dashBoardPresentationGraphicsQuality : 100 - sbf_homeScreenBlurGraphicsQuality : 100 - userInterfaceIdiom : 0 - _supportsDeepColor : false - name : iPhone - _keyboardGraphicsQuality : 100 - isGeneratingDeviceOrientationNotifications : true - orientation : 1 - _backlightLevel : 1 - isProximityMonitoringEnabled : false - systemVersion : 11.1.1 - _graphicsQuality : 100 - beginGeneratingDeviceOrientationNotifications : undefined - endGeneratingDeviceOrientationNotifications : undefined - buildVersion : 15C222 - systemName : iOS - _isSystemSoundEnabled : true - _feedbackSupportLevel : 1 - model : iPhone - _supportsForceTouch : true - localizedModel : iPhone - identifierForVendor : 4A7B44DB-AAAA-BBB-CCC-D8819581DDD - isBatteryMonitoringEnabled : false - batteryState : 0 - batteryLevel : -1 - proximityState : false - isMultitaskingSupported : true - playInputClick : undefined - _softwareDimmingAlpha : 0 - _playInputSelectSound : undefined - _playInputDeleteSound : undefined - _hasGraphicsQualityOverride : false - _hasTouchPad : false - _clearGraphicsQualityOverride : undefined - _predictionGraphicsQuality : 100 - _nativeScreenGamut : 0 - _tapticEngine : <_UITapticEngine: 0x1c06257c0> ```


⬆ トップに戻る

スクリーンショットを撮る```js

function screenshot() { ObjC.schedule(ObjC.mainQueue, function() { var getNativeFunction = function (ex, retVal, args) { return new NativeFunction(Module.findExportByName('UIKit', ex), retVal, args); }; var api = { UIWindow: ObjC.classes.UIWindow, UIGraphicsBeginImageContextWithOptions: getNativeFunction('UIGraphicsBeginImageContextWithOptions', 'void', [['double', 'double'], 'bool', 'double']), UIGraphicsBeginImageContextWithOptions: getNativeFunction('UIGraphicsBeginImageContextWithOptions', 'void', [['double', 'double'], 'bool', 'double']), UIGraphicsEndImageContext: getNativeFunction('UIGraphicsEndImageContext', 'void', []), UIGraphicsGetImageFromCurrentImageContext: getNativeFunction('UIGraphicsGetImageFromCurrentImageContext', 'pointer', []), UIImagePNGRepresentation: getNativeFunction('UIImagePNGRepresentation', 'pointer', ['pointer']) }; var view = api.UIWindow.keyWindow(); var bounds = view.bounds(); var size = bounds[1]; api.UIGraphicsBeginImageContextWithOptions(size, 0, 0); view.drawViewHierarchyInRect_afterScreenUpdates_(bounds, true);

root@kitploit:~
var image = api.UIGraphicsGetImageFromCurrentImageContext();
api.UIGraphicsEndImageContext();

var png = new ObjC.Object(api.UIImagePNGRepresentation(image));
send('screenshot', Memory.readByteArray(png.bytes(), png.length()));

}); }

rpc.exports = { takescreenshot: screenshot }

root@kitploit:~
1. 後で使用するために、ローカルスナップショットを .poly 形式でキャプチャします。
2. URLからリモートWebSocketストリーム設定を読み込み、PCAPに保存します。
3. オフライン再生のために、ローカルスナップショットまたはPCAPファイルを読み込みます。
4. ローカルサーバーにシグナルを送信して、*LiveShot* WebSocketリスナーポートを開閉します。
5. リアルタイム視覚化のために、ブラウザでローカル*LiveShot*ウェブクライアントを開きます。
6. ブラウザを開かずに、コマンドラインのみでローカル*LiveShot*ウェブクライアントを表示します。
7. ツールをクリーンにアンインストールします。
8. ツールのバージョンを表示します。```py
...
def save_screenshot(d):
    f = open('/tmp/screenshot.png', 'wb')
    f.write(d)
    f.close()

def on_message(msg, data):
    save_screenshot(data)
 
 script.exports.takescreenshot()
 
 # open screenshot & invoke rpc via input 
 # <Ctrl+C> will take screenshot, open it with eog & wait for export function name to invoke via input
 def on_message(msg, data):
    if 'payload' in msg:
        if msg['payload'] == 'screenshot':
            i = '/tmp/screenshot.png'
            f = open(i, 'wb')
            f.write(data)
            f.close()
            subprocess.call(['eog', i])
	   
 while True:
    try:
        time.sleep(1)
    except KeyboardInterrupt:
        script.exports.takescreenshot()
        try:
            getattr(script.exports, input())()
        except (KeyboardInterrupt, frida.core.RPCException) as e:
            print('[!]', e)

出力例 TODO


⬆ 先頭に戻る

SSHコマンドを記録する```js

Interceptor.attach(ObjC.classes.NMSSHChannel['- execute:error:timeout:'].implementation, {
onEnter: function(args) {
this.cmd = ObjC.Object(args[2]).toString(); this.timeout = args[4]; }, onLeave: function(retv) {
console.log(CMD: ${ObjC.Object(args[2]).toString()} Timeout: ${args[4]} Ret: ${retv}); }
});

root@kitploit:~
<details>
<summary>出力例</summary>
TODO
</details>

<br>[⬆ トップに戻る](#table-of-contents)

#### TODO
- GIFと例を追加
- /scriptsへのリンクを追加
- ユニバーサルSSLピン留め解除を拡張([ios](https://codeshare.frida.re/@dki/ios10-ssl-bypass/) [andoid 1](https://github.com/Fuzion24/JustTrustMe/blob/master/app/src/main/java/just/trust/me/Main.java) [android
ツールをダウンロード
  • Webview URLS
  • Print all runtime strings & stacktrace
  • Print shared preferences updates
  • String comparison
  • Hook JNI by address
  • Hook constructor
  • Hook Java reflection
  • Trace class
  • Hooking Unity3d
  • Get Android ID
  • Change location
  • Bypass FLAG_SECURE
  • Shared Preferences update
  • Hook all method overloads
  • Register broadcast receiver
  • Increase step count
  • インターフェースを実装するクラスをクラスローダーで一覧表示する
  • ファイルシステムアクセスフック $ frida --codeshare FrenchYeti/android-file-system-access-hook -f com.example.app --no-pause
  • Javaフックを削除/無効にする方法? null を implementation プロパティに割り当てます。
  • 出力例 TODO


    ⬆ Back to top

    クッキーの抽出```js

    var cookieJar = {}; var cookies = ObjC.classes.NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies(); for (var i = 0, l = cookies.count(); i < l; i++) { var cookie = cookies'- objectAtIndex:'; cookieJar[cookie.Name()] = cookie.Value().toString(); // "- expiresDate".toString() } console.log(JSON.stringify(cookieJar, null, 2));

    root@kitploit:~
    <details>
    <summary>出力例</summary>```js
    {
      "key1": "value 1",
      "key2": "value 2"
    }