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.5k4391年前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

PRs Welcome 以及输出示例

目录

原生
  • 加载 C/C++ 模块
  • 一次性监视点
  • 套接字活动
  • 拦截 open
  • 执行 shell 命令
  • 枚举模块
  • 记录 SQLite 查询
  • 记录方法参数
  • 拦截整个模块
  • 转储内存段
  • 内存扫描
  • Stalker
  • Cpp 名称反混淆
  • 早期钩子
Android
  • Binder 事务
  • 获取系统属性
  • 揭示手动注册的原生符号
  • 枚举已加载的类
  • 类描述
  • 关闭 WiFi
  • 设置代理
  • 获取 IMEI
  • 钩子 io InputStream
  • Android 弹 Toast
  • 等待特定模块加载
  • Webview URL
iOS
  • OS 日志
  • iOS 弹窗
  • 文件访问
  • 观察类
  • 查找应用 UUID
  • 提取 Cookie
  • 描述类成员
  • 类层次结构
  • 钩子反射
  • 设备属性
  • 截屏
  • 记录 SSH 命令
Windows

哈哈哈。不。

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$
root@kitploit:~
</details>


<details>
<summary>JEB</summary>	

使用键盘快捷键的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`)


</details>
<hr />
	

#### 获取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)
    }
  })
}


⬆ 返回顶部

Socket 活动```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


⬆ 返回顶部

拦截打开

一个拦截 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`


⬆ 返回顶部

执行shell命令```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>[⬆ Back to top](#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":""} .... ```


⬆ 返回顶部

Binder 事务```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...


⬆ 回到顶部

揭示原生方法

registerNativeMethods 可以作为针对原生.so库的反逆向技术使用,例如尽可能隐藏符号、混淆导出的符号,并最终在JNI桥上添加一些保护。 来源```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 ``` (此块内容为空,无翻译内容)```sh {"class":"org/chromium/net/GURLUtils","method":"nativeGetOrigin","signature":"(Ljava/lang/String;)Ljava/lang/String;","address":"0x..da910"} .. ```


⬆ Back to top

记录方法参数```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


⬆ 返回顶部

类描述

获取类的方法与成员。```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

它将在创建第一个Activity时关闭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);
输出示例 待办


⬆ Back to top

获取 IMEI

也可以钩取并修改 IMEI。```js function getIMEI(){ console.log('IMEI =', Java.use("android.telephony.TelephonyManager").$new().getDeviceId()); } Java.perform(getIMEI)

root@kitploit:~
<details>
<summary>输出示例</summary>
待办
</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


⬆ 返回顶部

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 URLS

记录当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


⬆ 返回顶部

按地址 Hook JNI

通过模块名和方法地址 Hook 原生方法,并打印参数。```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


⬆ 返回顶部

Hook 反射

`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 为 c'tor 添加追踪。```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/HEAD/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


⬆ 返回顶部

获取 Android ID

ANDROID_ID 在 Android 的每个应用程序中都是唯一的。```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;
	}
})
输出示例 待办


⬆ 返回顶部

绕过 FLAG_SECURE

绕过截图防护 stackoverflow 问题```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 重载```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>
待办事项
</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


⬆ Back to top

列出实现接口的类```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>[⬆ Back to top](#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();
    }
  });
});
输出示例 待办


⬆ 返回顶部

操作系统日志```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)

#### 观测类```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>[⬆ Back to top](#table-of-contents)


#### 查找iOS应用程序UUID

获取附加到应用时特定路径的UUID,通过读取每个应用容器下的plist文件。```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


⬆ Back to top

拦截整个模块

为了减少UI相关的功能,我使用以下步骤:

  1. 使用 -o /tmp/log1 将日志输出到文件
  2. 使用 $ sort /tmp/log1 | uniq -c | sort -rn | head -n20 | cut -d# -f2 | paste -sd "," - 将MRU复制到excludesList```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:~
├── [SCOPE]/
│   ├── domain.txt
│   └── output/
│       └── pwned
│           └── [DOMAIN].txt]

**PwnedOrNot** 还会使用作用域目录中的 `domain.txt` 文件。这样,就可以有多个```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)






#### C++ 名称反修饰器```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
        })
    }
})

Credit: 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:~
你现在已经准备好使用 `shell-ai`。获取更多帮助,请运行 `shell-ai --help`。

## 功能

- **自然语言转Shell命令**:只需用自然语言描述你想要做什么,`shell-ai` 就会生成相应的Shell命令。
- **自动执行模式**:可选地,在生成命令后,`shell-ai` 可以自动为你执行它。
- **多Shell支持**:支持不同的Shell环境,如 bash、zsh、fish 和 PowerShell。
- **上下文感知**:记住你之前命令的上下文,以提供更准确的建议。
- **安全第一**:详细的日志记录和确认提示,确保你了解即将运行的命令。
- **可插拔后端**:支持多种LLM提供商,如 OpenAI、Anthropic、Google、Groq、OpenRouter 以及任何兼容OpenAI的API。```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:~
". Actually the user message ends with "INPUT:\n\n". So the chunk content is empty? That can't be right. Perhaps the chunk is missing. I need to wait for the actual content. But the instruction says "This is chunk 162 of 166 from a longer Markdown document being translated in sequence." and then "INPUT:" with nothing. Possibly the user forgot to include the text. I should not make up content. I will respond with nothing? But the rules say "Return ONLY the translated text." If there is no input, there is nothing to translate. I should output nothing? That might be confusing.

Alternatively, perhaps the user intended to provide the chunk but it got lost. I'll check the raw message: "INPUT:\n\n" and then nothing. I think it's an empty chunk. The safest is to output an empty string. But the instruction says "Return ONLY the translated text." So I'll return nothing.

But wait: The user message ends with "INPUT:" and then a blank line, then " response". Actually after "INPUT:" there is a blank line, then "response" is not part of input, it's the start of my response. So the input is empty. So I will output nothing. However, to follow the rule "Return ONLY the translated text. No preamble", I should output an empty string. That might break concatenation but the chunk is empty, so it's fine.

Alternatively, perhaps the user meant that the chunk is the entire message below "INPUT:" but it's missing. I'll re-read: "INPUT:\n\nresponse". That's weird. I think it's an error. I'll proceed by assuming the chunk is empty. I will output nothing.

But to be safe, I can echo the empty input? No, that would be adding preamble. I'll output nothing.```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>
待办
</details>

<br>[⬆ 返回顶部](#table-of-contents)

#### 待办事项 
- 添加GIF和示例
- 添加链接到 /scripts
- 扩展通用SSL解除固定支持 [iOS](https://codeshare.frida.re/@dki/ios10-ssl-bypass/) [安卓 1](https://github.com/Fuzion24/JustTrustMe/blob/master/app/src/main/java/just/trust/me/Main.java) [安卓
下载工具
  • 打印所有运行时字符串及栈回溯
  • 打印 SharedPreferences 更新
  • 字符串比较
  • 按地址钩子 JNI
  • 钩子构造函数
  • 钩子 Java 反射
  • 跟踪类
  • 钩子 Unity3d
  • 获取 Android ID
  • 改变位置
  • 绕过 FLAG_SECURE
  • SharedPreferences 更新
  • 钩子所有方法重载
  • 注册广播接收器
  • 增加计步数
  • 列出实现接口的类(带类加载器)
  • 文件系统访问钩子 $ frida --codeshare FrenchYeti/android-file-system-access-hook -f com.example.app --no-pause
  • 如何移除/禁用 Java 钩子?将 implementation 属性赋值为 null。

  • ⬆ 返回顶部

    加载CPP模块```cpp

    #include #include

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

    root@kitploit:~
    ### [Blackeye](https://github.com/xo1337/Blackeye) - 最完整的基于 Bash 制作的钓鱼工具,拥有 32 个模板并与 ngrok 连接```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
    

    单个 TCP 会话。本文档的其余部分将继续阅读本文档的其余部分。```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"

    root@kitploit:~
    #### 加载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
    

    --bcol BCOL 背景颜色。支持十六进制,例如#00ff00。 --bclo BCLO 背景颜色不透明度。范围:0-100。 --bgra 以低亮度显示背景。可选。```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); }

    root@kitploit:~
    `invoke-build` 用于编译、执行 CI 任务、运行测试和生成文档。
    
    > `invoke-build` 会缓存构建脚本函数。如果你更改了构建脚本后出现问题,请尝试使用 `-NoBuildCaching` 参数运行 `invoke-build`。
    
    如果由于某种原因 `invoke-build` 不可用,可以使用仓库根目录下的 `build.ps1` 文件来编译项目。
    
    ### 手动构建
    
    要手动构建,请打开 PowerShell,导航到项目根目录,然后:
    
    - 运行 `dotnet restore`。
    - 运行 `dotnet build`。
    - 运行 `dotnet test`。
    
    这将生成解决方案并运行所有测试。
    
    ### CI/CD 元数据管道
    
    此仓库包含一些用于 CI/CD 的管道文件。
    
    - `GitHub Actions` 通过 `.github/workflows` 中的工作流进行配置。
      - [![codeql-analysis](https://github.com/microsoft/Microsoft.IO.RecyclableMemoryStream/workflows/codeql-analysis/badge.svg)](https://github.com/microsoft/Microsoft.IO.RecyclableMemoryStream/actions?query=workflow%3Acodeql-analysis)
      - [![dotnet](https://github.com/microsoft/Microsoft.IO.RecyclableMemoryStream/workflows/dotnet/badge.svg)](https://github.com/microsoft/Microsoft.IO.RecyclableMemoryStream/actions?query=workflow%3Adotnet)
    
    - `Azure DevOps` 管道通过 `/pipelines/` 文件夹中的 `.vsts-ci.yml` 文件进行配置。
      - 官方构建在 Azure DevOps 实例 `https://dev.azure.com/dnceng/internal` 中运行。
      - 官方构建管道是 `dotnet.project-system.rolling` 和 `dotnet.project-system.official`。
      - 这些是手动触发的(`dotnet.project-system.official` 也有计划触发器),并在 macOS、Linux 和 Windows 的组合上运行测试。
      - PR 验证在 `https://dev.azure.com/dnceng/public/` 中运行。管道名称是 `dotnet.project-system`。它在创建 PR 时触发。
      - 还有一个计划构建,每天午夜运行,在 `scheduled-build.yml` 中定义。```sh
    $ frida -Uf com.app --no-pause --enable-jit -e "Module.load('/data/local/tmp/a')"
    [ ] -> new NativeFunction(Module.findExportByName('a', 'test'), 'void', [])()
    


    ⬆ 返回顶部

    一次性监视点

    拦截 funcPtr 并记录谁读取/写入 x2,通过使用 mprotect 移除权限。```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'); } });

    root@kitploit:~
    <details>
    <summary>输出示例</summary>```
    [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
    
    输出示例 TODO


    ⬆ 返回顶部

    提取Cookie```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"
    }