Skip to content

REPL 命令注册表 objection/console/commands.py ​

commands.py 定义了 objection REPL 的命令注册表 COMMANDS——一个嵌套字典,把命令名映射到 meta(一句话说明)、exec(执行函数)、dynamic(动态补全函数)、flags(可补全的 flag)、commands(子命令)等元信息。Repl 在执行命令、CommandCompleter 在补全时都遍历这棵树。它是 objection 所有人类命令的单一事实来源。

📋 模块概览 ​

项目值
文件路径objection/console/commands.py
类型静态注册表(Python dict 字面量)
被谁调用Repl._find_command_exec_method / Repl._find_command_help、CommandCompleter.find_completions、agent_cli._enumerate_capabilities、agent_endpoints.capabilities
依赖objection.commands.*(各命令实现模块)与 objection.commands.android.* / objection.commands.ios.*

🎯 解决的问题 ​

  • 把"命令字符串 → 可调用 Python 函数"的映射集中在一处,避免散落各模块。
  • 用嵌套 commands 表达多级子命令(如 android hooking list classes),无需手写解析器。
  • 同时承载补全元数据(dynamic、flags)与帮助文本指针(帮助文本存于 helpfiles/*.txt)。
  • 让 Agent 能通过 agent capabilities / GET /capabilities 拿到完整能力清单。

🏗️ 核心结构 ​

节点 schema — 每个命令节点的字段 ​

源码注释:objection/console/commands.py:38-45

# meta: A small one-liner containing information about the command itself
# dynamic: A method to execute that would return completions to populate in the prompt
# exec: The *actual* method to execute when the command is issued.
# commands help is stored in the help files directory as a txt file.

典型叶子节点(commands.py:84-87):

python
'ping': {
    'meta': 'Ping the injected agent',
    'exec': frida_commands.ping
},

带子命令的中间节点(commands.py:263-276):

python
'jobs': {
    'meta': 'Work with objection jobs',
    'commands': {
        'list': {'meta': 'List all of the current jobs', 'exec': jobs.show},
        'kill': {'meta': 'Kill a job. This unloads the script',
                  'dynamic': jobs.list_current_jobs, 'exec': jobs.kill}
    }
},

带 flag 的节点(commands.py:227-231):

python
'search': {
    'meta': 'Search for pattern in the applications memory',
    'flags': ['--string', '--offsets-only'],
    'exec': memory.find_pattern
},

COMMANDS 顶层命令分组 ​

顶层 key源码位置说明
plugincommands.py:49-57插件加载
!commands.py:59-62执行 OS 命令(exec: None,由 Repl.run_command 特判)
reconnect / reconnect_spawn / resumecommands.py:64-77重连/重 spawn/恢复(exec: None,由 Repl 特判)
importcommands.py:79-82导入并运行 fridascript
pingcommands.py:84-87ping 注入的 agent
cd / ls / pwd / rmcommands.py:91-124,167-171文件系统导航
commandscommands.py:97-113命令历史(history / save / clear)
filesystemcommands.py:126-165cat / upload / download(含被注释掉的 http 子组)
env / frida / evaluatecommands.py:175-188环境信息、Frida 信息、JS 求值
memorycommands.py:192-245内存 dump / list / search / replace / write
sqlitecommands.py:249-259SQLite connect
jobscommands.py:263-276Job list / kill
uicommands.py:280-288通用 UI(alert)
androidcommands.py:292-518Android 全部子命令
ioscommands.py:520-772iOS 全部子命令
exitcommands.py:774-776退出(无 exec,Repl.run 特判)

Android 子命令树(commands.py:292-518) ​

android 节点下的二级命令:

二级 keyexec 来源说明
deoptimizegeneral.deoptimise强制 VM 解释执行
shell_execcommand.execute执行 shell 命令
hookingandroid_hooking.*list / watch / set / get / search / notify / generate
heapandroid_heap.*search / print / execute / evaluate
keystorekeystore.*list / detail / clear / watch
clipboardclipboard.monitor剪贴板监控
intentintents.*launch_activity / launch_service / implicit_intents
rootroot.*disable / simulate
sslpinningandroid_pinning.android_disable禁用 SSL pinning
proxyandroid_proxy.android_proxy_set设置代理
uiui.*screenshot / FLAG_SECURE

iOS 子命令树(commands.py:520-772) ​

ios 节点下的二级命令:

二级 keyexec 来源说明
infobinary.info二进制/dylib 信息
keychainkeychain.*dump / dump_raw / clear / remove / update / add
plistplist.cat读取 plist
bundlesbundles.*list_frameworks / list_bundles
nsuserdefaultsnsuserdefaults.getNSUserDefaults
nsurlcredentialstoragensurlcredentialstorage.dump凭证存储
cookiescookies.get共享 cookie
uiui.*alert / dump / screenshot / biometrics_bypass
heapios_heap.*print / search / execute / evaluate
hookingios_hooking.*list / watch / set / search / generate
pasteboardpasteboard.monitor剪贴板监控
sslpinningios_pinning.ios_disable禁用 SSL pinning
jailbreakjailbreak.*disable / simulate
monitorios_crypto.crypto_enableCommonCrypto 监控

dynamic 与 flags — 补全元数据 ​

dynamic 指向一个返回补全候选的函数,常用于文件系统类命令(按当前目录内容补全):

python
'cd': {
    'meta': 'Change the current working directory',
    'dynamic': filemanager.list_folders_in_current_fm_directory,
    'exec': filemanager.cd
},

flags 列出可补全的 --flag,CommandCompleter 会排除已输入的 flag:

python
'search': {
    'meta': 'Search for pattern in the applications memory',
    'flags': ['--string', '--offsets-only'],
    'exec': memory.find_pattern
},

⚙️ 实现要点 ​

  • 特判节点:!、reconnect、reconnect_spawn、resume、exit 的 exec 为 None 或缺失,由 Repl 在主循环 / run_command 中特判处理(见 repl.md)。
  • 帮助文本外置:注册表只存命令名,帮助文本存于 objection/console/helpfiles/*.txt,文件名由命令 token 用 . 连接(repl.py:267-268)。
  • 被注释的 http 子组:filesystem.http 整段被注释(commands.py:147-163),保留为待启用功能。
  • Agent 可发现性:_enumerate_capabilities(agent_cli.py:247)递归遍历 COMMANDS,输出扁平 {name, meta, has_exec, subcommands} 列表,供 agent capabilities 与 GET /capabilities 暴露给 AI Agent。
  • 平铺优于继承:注册表是纯数据字典,命令实现函数集中在 objection/commands/ 下,二者解耦——补全、执行、帮助各自遍历同一棵树。

🔍 源码索引 ​

符号位置
模块 import 块objection/console/commands.py:1-36
schema 注释objection/console/commands.py:38-45
COMMANDS 字典起始objection/console/commands.py:47
plugin / ! / reconnect / resume / import / pingobjection/console/commands.py:49-87
文件系统命令 cd/ls/pwd/rm/filesystemobjection/console/commands.py:91-171
env/frida/evaluateobjection/console/commands.py:175-188
memoryobjection/console/commands.py:192-245
sqlite / jobs / uiobjection/console/commands.py:249-288
android 子树objection/console/commands.py:292-518
ios 子树objection/console/commands.py:520-772
exitobjection/console/commands.py:774-776

🌳 COMMANDS 字典树形结构 ​

COMMANDS 是一棵深嵌套字典树。下图刻画节点类型与字段组合,以及"中间节点 vs 叶子节点"的区分规则。

节点字段语义与必选/可选关系:

字段适用节点必选?含义
meta所有推荐一句话说明,用于补全菜单与 agent capabilities
exec叶子是(除特判)命令执行函数,签名为 (arguments: list) -> None
commands中间是子命令字典,键为子命令名
dynamic叶子否返回补全候选词列表的函数(如按当前目录列出文件夹)
flags叶子否可补全的 --flag 列表,CommandCompleter 会排除已输入项

🎯 节点类型判定决策流 ​

Repl._find_command_exec_method 与 CommandCompleter.find_completions 都需要判定"当前 token 命中的节点是哪一类"。下图刻画该判定流程。

关键判定规则(基于 repl.py:214-221):

  • commands 优先于 exec:若节点同时含两者,遍历会下钻 commands 而忽略 exec。实际注册表中 android、ios、memory、jobs 等中间节点都只有 commands,无 exec,所以不会出现歧义;但 schema 层面并不禁止二者并存。
  • exec: None 等价于无 exec:!、reconnect、reconnect_spawn、resume 显式写 exec: None(commands.py:59-77),exit 干脆不写 exec(commands.py:774)。两者在遍历时都被视为"无 exec",由 Repl 特判。
  • 补全器视角不同:CommandCompleter 遇到 commands 子键会列出子命令名作为候选;遇到 flags 会列出未输入的 flag;遇到 dynamic 会调用该函数取候选。三类候选在 find_completions 中分别处理。

📐 注册表内存布局(ASCII 框图) ​

下图用一个具体命令 android hooking list classes 的注册路径,展示字典嵌套的内存结构与遍历指针移动。

COMMANDS (dict)
│
├── "plugin"      : {meta, commands:{...}}
├── "!"           : {meta, exec:None}        ← 特判
├── "reconnect"   : {meta, exec:None}        ← 特判
├── "ping"        : {meta, exec:frida_commands.ping}
│
├── "android"     : {meta, commands:{                ← 中间节点, 下钻
│       │
│       ├── "deoptimize": {meta, exec:general.deoptimise}
│       │
│       ├── "hooking"  : {meta, commands:{           ← 中间节点, 下钻
│       │       │
│       │       ├── "list": {meta, commands:{        ← 中间节点, 下钻
│       │       │       │
│       │       │       ├── "classes": {
│       │       │       │     meta:'List the currently loaded classes',
│       │       │       │     exec: android_hooking.show_android_classes  ← 叶子
│       │       │       │ }
│       │       │       ├── "class_methods": {meta, exec:...}
│       │       │       ├── "activities":    {meta, exec:...}
│       │       │       └── ... (receivers/services/class_loaders)
│       │       │ }
│       │       ├── "watch": {meta, exec:..., flags:[--dump-args,...]}
│       │       ├── "set":  {meta, commands:{ return_value:{...} }}
│       │       └── ... (get/search/notify/generate)
│       │ }
│       ├── "heap":     {meta, commands:{ search/print/execute/evaluate }}
│       ├── "keystore": {meta, commands:{ list/detail/clear/watch }}
│       └── ... (clipboard/intent/root/sslpinning/proxy/ui)
│   }}
│
├── "ios":   {meta, commands:{...}}   ← 与 android 同构
├── "memory":{meta, commands:{ dump/list/search/replace/write }}
└── "exit":  {meta}                   ← 无 exec, 特判

遍历指针移动(输入 "android hooking list classes"):
  dict_to_walk = COMMANDS
  token="android"  → 命中, 有 commands → 下钻
  dict_to_walk = COMMANDS["android"]["commands"]
  token="hooking" → 命中, 有 commands → 下钻
  dict_to_walk = ...["hooking"]["commands"]
  token="list"    → 命中, 有 commands → 下钻
  dict_to_walk = ...["list"]["commands"]
  token="classes" → 命中, 无 commands, 有 exec → 取 exec, break
  walked_tokens=4, exec_method=show_android_classes
  arguments = tokens[4:] = []   ← 无额外参数

🔗 相关文档 ​

基于 GPL-3.0-or-later 许可发布