From dbee6d0826ff9c9359ecd752c243447954ca129a Mon Sep 17 00:00:00 2001 From: CKCat Date: Sat, 29 Nov 2025 17:26:42 +0800 Subject: [PATCH] feat: Add Chinese (cn) localization for website posts and documentation, and update site configuration. --- _i18n/cn.yml | 54 + _i18n/cn/_docs/android.md | 93 + _i18n/cn/_docs/best-practices.md | 71 + _i18n/cn/_docs/bridges.md | 176 + _i18n/cn/_docs/building.md | 100 + _i18n/cn/_docs/c-api.md | 33 + _i18n/cn/_docs/contributing.md | 18 + _i18n/cn/_docs/examples/android.md | 107 + _i18n/cn/_docs/examples/ios.md | 142 + _i18n/cn/_docs/examples/javascript.md | 105 + _i18n/cn/_docs/examples/linux.md | 6 + _i18n/cn/_docs/examples/macos.md | 44 + _i18n/cn/_docs/examples/windows.md | 82 + _i18n/cn/_docs/footprint.md | 93 + _i18n/cn/_docs/frida-cli.md | 79 + _i18n/cn/_docs/frida-discover.md | 1 + _i18n/cn/_docs/frida-kill.md | 38 + _i18n/cn/_docs/frida-ls-devices.md | 17 + _i18n/cn/_docs/frida-ps.md | 17 + _i18n/cn/_docs/frida-trace.md | 312 ++ .../session-initialization-primer.md | 366 ++ _i18n/cn/_docs/functions.md | 391 ++ _i18n/cn/_docs/gadget.md | 263 ++ _i18n/cn/_docs/go-api.md | 49 + _i18n/cn/_docs/gsoc-ideas-2015.md | 117 + _i18n/cn/_docs/gsod-ideas-2023.md | 135 + _i18n/cn/_docs/gum-graft.md | 21 + _i18n/cn/_docs/hacking.md | 50 + _i18n/cn/_docs/history.md | 24 + _i18n/cn/_docs/index.md | 43 + _i18n/cn/_docs/installation.md | 84 + _i18n/cn/_docs/ios.md | 91 + _i18n/cn/_docs/javascript-api.md | 4152 +++++++++++++++++ _i18n/cn/_docs/messages.md | 183 + _i18n/cn/_docs/modes.md | 25 + _i18n/cn/_docs/presentations.md | 104 + _i18n/cn/_docs/quickstart.md | 84 + _i18n/cn/_docs/stalker.md | 2329 +++++++++ _i18n/cn/_docs/swift-api.md | 7 + _i18n/cn/_docs/troubleshooting.md | 36 + .../2014-01-05-frida-1-0-5-released.markdown | 13 + .../2014-01-11-frida-1-0-6-released.markdown | 15 + .../2014-01-12-frida-1-0-7-released.markdown | 27 + .../2014-01-15-frida-1-0-8-released.markdown | 13 + .../2014-01-25-frida-1-0-9-released.markdown | 30 + .../2014-02-16-frida-1-0-10-released.markdown | 46 + .../2014-03-09-frida-1-0-11-released.markdown | 17 + .../2014-04-17-frida-1-2-0-released.markdown | 14 + .../2014-04-21-frida-1-2-1-released.markdown | 14 + .../2014-05-04-frida-1-4-0-released.markdown | 12 + .../2014-05-14-frida-1-4-1-released.markdown | 12 + .../2014-05-14-frida-1-4-2-released.markdown | 12 + .../2014-05-29-frida-1-6-0-released.markdown | 48 + .../2014-07-26-frida-1-6-1-released.markdown | 23 + .../2014-08-03-frida-1-6-2-released.markdown | 27 + .../2014-08-24-frida-1-6-3-released.markdown | 30 + .../2014-10-19-frida-1-6-4-released.markdown | 27 + .../2014-10-29-frida-1-6-5-released.markdown | 15 + .../2014-11-03-frida-1-6-6-released.markdown | 10 + .../2014-11-03-frida-1-6-7-released.markdown | 16 + .../2014-11-18-frida-1-6-8-released.markdown | 10 + .../2015-02-23-frida-1-8-0-released.markdown | 10 + .../2015-03-01-frida-2-0-0-released.markdown | 39 + .../2015-03-01-frida-2-0-1-released.markdown | 10 + .../2015-03-01-frida-2-0-2-released.markdown | 12 + .../2015-03-20-frida-3-0-0-released.markdown | 42 + .../2015-05-08-frida-4-0-0-released.markdown | 157 + .../2015-06-09-frida-4-1-released.markdown | 253 + .../2015-06-18-frida-4-2-released.markdown | 153 + .../2015-07-15-frida-4-3-released.markdown | 58 + .../2015-07-31-frida-4-4-released.markdown | 20 + .../2015-09-10-frida-4-5-released.markdown | 45 + .../2015-09-17-frida-5-0-released.markdown | 95 + .../2015-11-11-frida-6-0-released.markdown | 110 + .../2016-01-14-frida-6-1-released.markdown | 71 + ...6-01-31-frida-fosdem-presentation.markdown | 18 + .../2016-02-01-frida-6-2-released.markdown | 93 + .../2016-02-24-frida-7-0-released.markdown | 85 + .../2016-04-04-frida-7-1-released.markdown | 107 + .../2016-06-02-frida-7-2-released.markdown | 234 + .../2016-08-15-frida-7-3-released.markdown | 48 + .../2016-10-04-frida-8-0-released.markdown | 64 + .../2016-10-25-frida-8-1-released.markdown | 251 + .../2017-01-09-frida-9-0-released.markdown | 116 + .../2017-05-02-frida-10-0-released.markdown | 23 + .../2017-08-15-frida-10-4-released.markdown | 128 + .../2017-08-25-frida-10-5-released.markdown | 101 + .../2017-09-29-frida-10-6-released.markdown | 20 + .../2018-03-13-frida-10-7-released.markdown | 16 + .../2018-04-28-frida-10-8-released.markdown | 173 + .../2018-05-05-frida-11-0-released.markdown | 280 ++ .../2018-07-12-frida-12-0-released.markdown | 150 + .../2018-08-25-frida-12-1-released.markdown | 47 + .../2018-09-11-frida-12-2-released.markdown | 55 + .../2019-05-15-frida-12-5-released.markdown | 147 + ...2019-05-15-nowsecure-connect-2019.markdown | 12 + .../2019-05-28-frida-12-6-released.markdown | 164 + .../2019-09-18-frida-12-7-released.markdown | 460 ++ .../2019-12-18-frida-12-8-released.markdown | 271 ++ .../2020-05-19-frida-12-9-released.markdown | 142 + .../2020-06-29-frida-12-10-released.markdown | 193 + .../2020-07-24-frida-12-11-released.markdown | 183 + .../2020-10-28-frida-14-0-released.markdown | 164 + .../2020-12-01-frida-14-1-released.markdown | 131 + .../2021-02-05-frida-14-2-released.markdown | 333 ++ .../2021-07-18-frida-15-0-released.markdown | 586 +++ .../2021-09-03-frida-15-1-released.markdown | 191 + ...2022-02-01-frida-15-1-15-released.markdown | 193 + ...2022-02-03-frida-15-1-16-released.markdown | 21 + ...2022-02-10-frida-15-1-17-released.markdown | 36 + ...2022-05-04-frida-15-1-18-released.markdown | 81 + ...2022-05-04-frida-15-1-19-released.markdown | 17 + ...2022-05-06-frida-15-1-20-released.markdown | 18 + ...2022-05-06-frida-15-1-21-released.markdown | 16 + ...2022-05-09-frida-15-1-22-released.markdown | 18 + ...2022-05-30-frida-15-1-23-released.markdown | 49 + ...2022-06-03-frida-15-1-24-released.markdown | 15 + ...2022-06-18-frida-15-1-25-released.markdown | 61 + ...2022-06-20-frida-15-1-26-released.markdown | 18 + ...2022-06-20-frida-15-1-27-released.markdown | 18 + ...2022-07-06-frida-15-1-28-released.markdown | 118 + .../2022-07-21-frida-15-2-0-released.markdown | 300 ++ .../2022-07-21-frida-15-2-1-released.markdown | 16 + .../2022-07-21-frida-15-2-2-released.markdown | 16 + .../2022-10-08-frida-16-0-0-released.markdown | 153 + .../2022-10-08-frida-16-0-1-released.markdown | 13 + .../2022-10-22-frida-16-0-2-released.markdown | 35 + .../2022-11-23-frida-16-0-3-released.markdown | 106 + .../2022-11-26-frida-16-0-4-released.markdown | 23 + .../2022-11-28-frida-16-0-5-released.markdown | 10 + .../2022-12-01-frida-16-0-6-released.markdown | 13 + .../2022-12-02-frida-16-0-7-released.markdown | 58 + .../2022-12-13-frida-16-0-8-released.markdown | 36 + .../2023-02-11-frida-16-0-9-released.markdown | 42 + ...2023-02-17-frida-16-0-10-released.markdown | 30 + ...2023-03-10-frida-16-0-11-released.markdown | 19 + ...2023-04-14-frida-16-0-12-released.markdown | 52 + ...2023-04-15-frida-16-0-13-released.markdown | 14 + ...2023-04-18-frida-16-0-14-released.markdown | 20 + ...2023-04-19-frida-16-0-15-released.markdown | 16 + ...2023-04-19-frida-16-0-16-released.markdown | 19 + ...2023-04-19-frida-16-0-17-released.markdown | 18 + ...2023-04-22-frida-16-0-18-released.markdown | 13 + ...2023-04-27-frida-16-0-19-released.markdown | 28 + .../2023-06-23-frida-16-1-0-released.markdown | 550 +++ .../2023-07-01-frida-16-1-1-released.markdown | 18 + .../2023-07-11-frida-16-1-2-released.markdown | 17 + .../2023-07-14-frida-16-1-3-released.markdown | 21 + .../2023-08-29-frida-16-1-4-released.markdown | 21 + .../2023-11-04-frida-16-1-5-released.markdown | 82 + .../2023-11-13-frida-16-1-6-released.markdown | 16 + .../2023-11-16-frida-16-1-7-released.markdown | 21 + .../2023-11-28-frida-16-1-8-released.markdown | 17 + .../2023-12-20-frida-16-1-9-released.markdown | 22 + ...2023-12-24-frida-16-1-10-released.markdown | 18 + ...2024-01-12-frida-16-1-11-released.markdown | 30 + .../2024-02-16-frida-16-2-0-released.markdown | 127 + .../2024-02-16-frida-16-2-1-released.markdown | 15 + .../2024-05-17-frida-16-2-2-released.markdown | 167 + .../2024-05-18-frida-16-2-3-released.markdown | 17 + .../2024-05-20-frida-16-2-4-released.markdown | 15 + .../2024-05-21-frida-16-2-5-released.markdown | 19 + .../2024-05-31-frida-16-3-0-released.markdown | 320 ++ .../2024-05-31-frida-16-3-1-released.markdown | 15 + .../2024-06-04-frida-16-3-2-released.markdown | 29 + .../2024-06-04-frida-16-3-3-released.markdown | 10 + .../2024-07-05-frida-16-4-0-released.markdown | 52 + .../2024-07-06-frida-16-4-1-released.markdown | 13 + .../2024-07-08-frida-16-4-2-released.markdown | 13 + .../2024-07-13-frida-16-4-3-released.markdown | 24 + .../2024-07-16-frida-16-4-4-released.markdown | 26 + .../2024-07-17-frida-16-4-5-released.markdown | 17 + .../2024-07-22-frida-16-4-6-released.markdown | 22 + .../2024-07-23-frida-16-4-7-released.markdown | 15 + .../2024-08-02-frida-16-4-8-released.markdown | 18 + .../2024-08-20-frida-16-4-9-released.markdown | 19 + ...2024-08-22-frida-16-4-10-released.markdown | 18 + .../2024-09-06-frida-16-5-0-released.markdown | 204 + .../_posts/2024-09-06-frida-16-5-1.markdown | 10 + .../_posts/2024-09-19-frida-16-5-2.markdown | 21 + .../_posts/2024-10-10-frida-16-5-3.markdown | 30 + .../_posts/2024-10-10-frida-16-5-4.markdown | 10 + .../_posts/2024-10-11-frida-16-5-5.markdown | 13 + .../_posts/2024-10-14-frida-16-5-6.markdown | 17 + .../_posts/2024-11-14-frida-16-5-7.markdown | 55 + .../2024-12-09-frida-16-5-8-released.markdown | 37 + .../2024-12-09-frida-16-5-9-released.markdown | 16 + .../2025-01-09-frida-16-6-0-released.markdown | 34 + .../2025-01-10-frida-16-6-1-released.markdown | 20 + .../2025-01-13-frida-16-6-2-released.markdown | 16 + .../2025-01-14-frida-16-6-3-released.markdown | 12 + .../2025-01-17-frida-16-6-4-released.markdown | 17 + .../2025-01-23-frida-16-6-5-released.markdown | 25 + .../2025-01-27-frida-16-6-6-released.markdown | 20 + .../2025-03-13-frida-16-7-0-released.markdown | 176 + .../2025-03-21-frida-16-7-1-released.markdown | 41 + .../2025-03-21-frida-16-7-2-released.markdown | 14 + .../2025-03-21-frida-16-7-3-released.markdown | 14 + .../2025-03-31-frida-16-7-4-released.markdown | 22 + .../2025-04-05-frida-16-7-5-released.markdown | 27 + .../2025-04-05-frida-16-7-6-released.markdown | 12 + .../2025-04-05-frida-16-7-7-released.markdown | 12 + .../2025-04-07-frida-16-7-8-released.markdown | 17 + .../2025-04-07-frida-16-7-9-released.markdown | 15 + ...2025-04-09-frida-16-7-10-released.markdown | 21 + ...2025-04-14-frida-16-7-11-released.markdown | 23 + ...2025-04-16-frida-16-7-12-released.markdown | 17 + ...2025-04-17-frida-16-7-13-released.markdown | 12 + ...2025-04-25-frida-16-7-14-released.markdown | 22 + ...2025-05-08-frida-16-7-15-released.markdown | 19 + ...2025-05-08-frida-16-7-16-released.markdown | 12 + ...2025-05-09-frida-16-7-17-released.markdown | 17 + ...2025-05-09-frida-16-7-18-released.markdown | 15 + ...2025-05-14-frida-16-7-19-released.markdown | 15 + .../2025-05-17-frida-17-0-0-released.markdown | 177 + .../2025-05-17-frida-17-0-1-released.markdown | 17 + .../2025-05-20-frida-17-0-2-released.markdown | 14 + .../2025-05-20-frida-17-0-3-released.markdown | 12 + .../2025-05-22-frida-17-0-4-released.markdown | 13 + .../2025-05-24-frida-17-0-5-released.markdown | 10 + .../2025-05-28-frida-17-0-6-released.markdown | 14 + .../2025-05-29-frida-17-0-7-released.markdown | 16 + .../2025-06-05-frida-17-1-0-released.markdown | 31 + .../2025-06-06-frida-17-1-1-released.markdown | 21 + .../2025-06-06-frida-17-1-2-released.markdown | 22 + .../2025-06-07-frida-17-1-3-released.markdown | 15 + .../2025-06-10-frida-17-1-4-released.markdown | 24 + .../2025-06-13-frida-17-1-5-released.markdown | 10 + .../2025-06-18-frida-17-2-0-released.markdown | 170 + .../2025-06-19-frida-17-2-1-released.markdown | 16 + .../2025-06-19-frida-17-2-2-released.markdown | 13 + .../2025-06-20-frida-17-2-3-released.markdown | 16 + .../2025-06-20-frida-17-2-4-released.markdown | 16 + .../2025-06-23-frida-17-2-5-released.markdown | 25 + .../2025-06-27-frida-17-2-6-released.markdown | 27 + .../2025-07-01-frida-17-2-7-released.markdown | 22 + .../2025-07-02-frida-17-2-8-released.markdown | 12 + .../2025-07-02-frida-17-2-9-released.markdown | 18 + ...2025-07-03-frida-17-2-10-released.markdown | 19 + ...2025-07-03-frida-17-2-11-released.markdown | 19 + ...2025-07-18-frida-17-2-12-released.markdown | 46 + ...2025-07-21-frida-17-2-13-released.markdown | 16 + ...2025-07-24-frida-17-2-14-released.markdown | 37 + ...2025-08-02-frida-17-2-15-released.markdown | 27 + ...2025-08-12-frida-17-2-16-released.markdown | 19 + ...2025-08-20-frida-17-2-17-released.markdown | 15 + .../2025-09-15-frida-17-3-0-released.markdown | 23 + .../2025-09-18-frida-17-3-1-released.markdown | 10 + .../2025-09-19-frida-17-3-2-released.markdown | 16 + .../2025-10-12-frida-17-4-0-released.markdown | 36 + .../2025-10-24-frida-17-4-1-released.markdown | 23 + .../2025-10-29-frida-17-4-2-released.markdown | 19 + .../2025-10-31-frida-17-4-3-released.markdown | 22 + .../2025-11-01-frida-17-4-4-released.markdown | 15 + .../2025-11-04-frida-17-5-0-released.markdown | 143 + .../2025-11-05-frida-17-5-1-released.markdown | 19 + 256 files changed, 22502 insertions(+) create mode 100644 _i18n/cn.yml create mode 100644 _i18n/cn/_docs/android.md create mode 100644 _i18n/cn/_docs/best-practices.md create mode 100644 _i18n/cn/_docs/bridges.md create mode 100644 _i18n/cn/_docs/building.md create mode 100644 _i18n/cn/_docs/c-api.md create mode 100644 _i18n/cn/_docs/contributing.md create mode 100644 _i18n/cn/_docs/examples/android.md create mode 100644 _i18n/cn/_docs/examples/ios.md create mode 100644 _i18n/cn/_docs/examples/javascript.md create mode 100644 _i18n/cn/_docs/examples/linux.md create mode 100644 _i18n/cn/_docs/examples/macos.md create mode 100644 _i18n/cn/_docs/examples/windows.md create mode 100644 _i18n/cn/_docs/footprint.md create mode 100644 _i18n/cn/_docs/frida-cli.md create mode 100644 _i18n/cn/_docs/frida-discover.md create mode 100644 _i18n/cn/_docs/frida-kill.md create mode 100644 _i18n/cn/_docs/frida-ls-devices.md create mode 100644 _i18n/cn/_docs/frida-ps.md create mode 100644 _i18n/cn/_docs/frida-trace.md create mode 100644 _i18n/cn/_docs/frida-trace/session-initialization-primer.md create mode 100644 _i18n/cn/_docs/functions.md create mode 100644 _i18n/cn/_docs/gadget.md create mode 100644 _i18n/cn/_docs/go-api.md create mode 100644 _i18n/cn/_docs/gsoc-ideas-2015.md create mode 100644 _i18n/cn/_docs/gsod-ideas-2023.md create mode 100644 _i18n/cn/_docs/gum-graft.md create mode 100644 _i18n/cn/_docs/hacking.md create mode 100644 _i18n/cn/_docs/history.md create mode 100644 _i18n/cn/_docs/index.md create mode 100644 _i18n/cn/_docs/installation.md create mode 100644 _i18n/cn/_docs/ios.md create mode 100644 _i18n/cn/_docs/javascript-api.md create mode 100644 _i18n/cn/_docs/messages.md create mode 100644 _i18n/cn/_docs/modes.md create mode 100644 _i18n/cn/_docs/presentations.md create mode 100644 _i18n/cn/_docs/quickstart.md create mode 100644 _i18n/cn/_docs/stalker.md create mode 100644 _i18n/cn/_docs/swift-api.md create mode 100644 _i18n/cn/_docs/troubleshooting.md create mode 100644 _i18n/cn/_posts/2014-01-05-frida-1-0-5-released.markdown create mode 100644 _i18n/cn/_posts/2014-01-11-frida-1-0-6-released.markdown create mode 100644 _i18n/cn/_posts/2014-01-12-frida-1-0-7-released.markdown create mode 100644 _i18n/cn/_posts/2014-01-15-frida-1-0-8-released.markdown create mode 100644 _i18n/cn/_posts/2014-01-25-frida-1-0-9-released.markdown create mode 100644 _i18n/cn/_posts/2014-02-16-frida-1-0-10-released.markdown create mode 100644 _i18n/cn/_posts/2014-03-09-frida-1-0-11-released.markdown create mode 100644 _i18n/cn/_posts/2014-04-17-frida-1-2-0-released.markdown create mode 100644 _i18n/cn/_posts/2014-04-21-frida-1-2-1-released.markdown create mode 100644 _i18n/cn/_posts/2014-05-04-frida-1-4-0-released.markdown create mode 100644 _i18n/cn/_posts/2014-05-14-frida-1-4-1-released.markdown create mode 100644 _i18n/cn/_posts/2014-05-14-frida-1-4-2-released.markdown create mode 100644 _i18n/cn/_posts/2014-05-29-frida-1-6-0-released.markdown create mode 100644 _i18n/cn/_posts/2014-07-26-frida-1-6-1-released.markdown create mode 100644 _i18n/cn/_posts/2014-08-03-frida-1-6-2-released.markdown create mode 100644 _i18n/cn/_posts/2014-08-24-frida-1-6-3-released.markdown create mode 100644 _i18n/cn/_posts/2014-10-19-frida-1-6-4-released.markdown create mode 100644 _i18n/cn/_posts/2014-10-29-frida-1-6-5-released.markdown create mode 100644 _i18n/cn/_posts/2014-11-03-frida-1-6-6-released.markdown create mode 100644 _i18n/cn/_posts/2014-11-03-frida-1-6-7-released.markdown create mode 100644 _i18n/cn/_posts/2014-11-18-frida-1-6-8-released.markdown create mode 100644 _i18n/cn/_posts/2015-02-23-frida-1-8-0-released.markdown create mode 100644 _i18n/cn/_posts/2015-03-01-frida-2-0-0-released.markdown create mode 100644 _i18n/cn/_posts/2015-03-01-frida-2-0-1-released.markdown create mode 100644 _i18n/cn/_posts/2015-03-01-frida-2-0-2-released.markdown create mode 100644 _i18n/cn/_posts/2015-03-20-frida-3-0-0-released.markdown create mode 100644 _i18n/cn/_posts/2015-05-08-frida-4-0-0-released.markdown create mode 100644 _i18n/cn/_posts/2015-06-09-frida-4-1-released.markdown create mode 100644 _i18n/cn/_posts/2015-06-18-frida-4-2-released.markdown create mode 100644 _i18n/cn/_posts/2015-07-15-frida-4-3-released.markdown create mode 100644 _i18n/cn/_posts/2015-07-31-frida-4-4-released.markdown create mode 100644 _i18n/cn/_posts/2015-09-10-frida-4-5-released.markdown create mode 100644 _i18n/cn/_posts/2015-09-17-frida-5-0-released.markdown create mode 100644 _i18n/cn/_posts/2015-11-11-frida-6-0-released.markdown create mode 100644 _i18n/cn/_posts/2016-01-14-frida-6-1-released.markdown create mode 100644 _i18n/cn/_posts/2016-01-31-frida-fosdem-presentation.markdown create mode 100644 _i18n/cn/_posts/2016-02-01-frida-6-2-released.markdown create mode 100644 _i18n/cn/_posts/2016-02-24-frida-7-0-released.markdown create mode 100644 _i18n/cn/_posts/2016-04-04-frida-7-1-released.markdown create mode 100644 _i18n/cn/_posts/2016-06-02-frida-7-2-released.markdown create mode 100644 _i18n/cn/_posts/2016-08-15-frida-7-3-released.markdown create mode 100644 _i18n/cn/_posts/2016-10-04-frida-8-0-released.markdown create mode 100644 _i18n/cn/_posts/2016-10-25-frida-8-1-released.markdown create mode 100644 _i18n/cn/_posts/2017-01-09-frida-9-0-released.markdown create mode 100644 _i18n/cn/_posts/2017-05-02-frida-10-0-released.markdown create mode 100644 _i18n/cn/_posts/2017-08-15-frida-10-4-released.markdown create mode 100644 _i18n/cn/_posts/2017-08-25-frida-10-5-released.markdown create mode 100644 _i18n/cn/_posts/2017-09-29-frida-10-6-released.markdown create mode 100644 _i18n/cn/_posts/2018-03-13-frida-10-7-released.markdown create mode 100644 _i18n/cn/_posts/2018-04-28-frida-10-8-released.markdown create mode 100644 _i18n/cn/_posts/2018-05-05-frida-11-0-released.markdown create mode 100644 _i18n/cn/_posts/2018-07-12-frida-12-0-released.markdown create mode 100644 _i18n/cn/_posts/2018-08-25-frida-12-1-released.markdown create mode 100644 _i18n/cn/_posts/2018-09-11-frida-12-2-released.markdown create mode 100644 _i18n/cn/_posts/2019-05-15-frida-12-5-released.markdown create mode 100644 _i18n/cn/_posts/2019-05-15-nowsecure-connect-2019.markdown create mode 100644 _i18n/cn/_posts/2019-05-28-frida-12-6-released.markdown create mode 100644 _i18n/cn/_posts/2019-09-18-frida-12-7-released.markdown create mode 100644 _i18n/cn/_posts/2019-12-18-frida-12-8-released.markdown create mode 100644 _i18n/cn/_posts/2020-05-19-frida-12-9-released.markdown create mode 100644 _i18n/cn/_posts/2020-06-29-frida-12-10-released.markdown create mode 100644 _i18n/cn/_posts/2020-07-24-frida-12-11-released.markdown create mode 100644 _i18n/cn/_posts/2020-10-28-frida-14-0-released.markdown create mode 100644 _i18n/cn/_posts/2020-12-01-frida-14-1-released.markdown create mode 100644 _i18n/cn/_posts/2021-02-05-frida-14-2-released.markdown create mode 100644 _i18n/cn/_posts/2021-07-18-frida-15-0-released.markdown create mode 100644 _i18n/cn/_posts/2021-09-03-frida-15-1-released.markdown create mode 100644 _i18n/cn/_posts/2022-02-01-frida-15-1-15-released.markdown create mode 100644 _i18n/cn/_posts/2022-02-03-frida-15-1-16-released.markdown create mode 100644 _i18n/cn/_posts/2022-02-10-frida-15-1-17-released.markdown create mode 100644 _i18n/cn/_posts/2022-05-04-frida-15-1-18-released.markdown create mode 100644 _i18n/cn/_posts/2022-05-04-frida-15-1-19-released.markdown create mode 100644 _i18n/cn/_posts/2022-05-06-frida-15-1-20-released.markdown create mode 100644 _i18n/cn/_posts/2022-05-06-frida-15-1-21-released.markdown create mode 100644 _i18n/cn/_posts/2022-05-09-frida-15-1-22-released.markdown create mode 100644 _i18n/cn/_posts/2022-05-30-frida-15-1-23-released.markdown create mode 100644 _i18n/cn/_posts/2022-06-03-frida-15-1-24-released.markdown create mode 100644 _i18n/cn/_posts/2022-06-18-frida-15-1-25-released.markdown create mode 100644 _i18n/cn/_posts/2022-06-20-frida-15-1-26-released.markdown create mode 100644 _i18n/cn/_posts/2022-06-20-frida-15-1-27-released.markdown create mode 100644 _i18n/cn/_posts/2022-07-06-frida-15-1-28-released.markdown create mode 100644 _i18n/cn/_posts/2022-07-21-frida-15-2-0-released.markdown create mode 100644 _i18n/cn/_posts/2022-07-21-frida-15-2-1-released.markdown create mode 100644 _i18n/cn/_posts/2022-07-21-frida-15-2-2-released.markdown create mode 100644 _i18n/cn/_posts/2022-10-08-frida-16-0-0-released.markdown create mode 100644 _i18n/cn/_posts/2022-10-08-frida-16-0-1-released.markdown create mode 100644 _i18n/cn/_posts/2022-10-22-frida-16-0-2-released.markdown create mode 100644 _i18n/cn/_posts/2022-11-23-frida-16-0-3-released.markdown create mode 100644 _i18n/cn/_posts/2022-11-26-frida-16-0-4-released.markdown create mode 100644 _i18n/cn/_posts/2022-11-28-frida-16-0-5-released.markdown create mode 100644 _i18n/cn/_posts/2022-12-01-frida-16-0-6-released.markdown create mode 100644 _i18n/cn/_posts/2022-12-02-frida-16-0-7-released.markdown create mode 100644 _i18n/cn/_posts/2022-12-13-frida-16-0-8-released.markdown create mode 100644 _i18n/cn/_posts/2023-02-11-frida-16-0-9-released.markdown create mode 100644 _i18n/cn/_posts/2023-02-17-frida-16-0-10-released.markdown create mode 100644 _i18n/cn/_posts/2023-03-10-frida-16-0-11-released.markdown create mode 100644 _i18n/cn/_posts/2023-04-14-frida-16-0-12-released.markdown create mode 100644 _i18n/cn/_posts/2023-04-15-frida-16-0-13-released.markdown create mode 100644 _i18n/cn/_posts/2023-04-18-frida-16-0-14-released.markdown create mode 100644 _i18n/cn/_posts/2023-04-19-frida-16-0-15-released.markdown create mode 100644 _i18n/cn/_posts/2023-04-19-frida-16-0-16-released.markdown create mode 100644 _i18n/cn/_posts/2023-04-19-frida-16-0-17-released.markdown create mode 100644 _i18n/cn/_posts/2023-04-22-frida-16-0-18-released.markdown create mode 100644 _i18n/cn/_posts/2023-04-27-frida-16-0-19-released.markdown create mode 100644 _i18n/cn/_posts/2023-06-23-frida-16-1-0-released.markdown create mode 100644 _i18n/cn/_posts/2023-07-01-frida-16-1-1-released.markdown create mode 100644 _i18n/cn/_posts/2023-07-11-frida-16-1-2-released.markdown create mode 100644 _i18n/cn/_posts/2023-07-14-frida-16-1-3-released.markdown create mode 100644 _i18n/cn/_posts/2023-08-29-frida-16-1-4-released.markdown create mode 100644 _i18n/cn/_posts/2023-11-04-frida-16-1-5-released.markdown create mode 100644 _i18n/cn/_posts/2023-11-13-frida-16-1-6-released.markdown create mode 100644 _i18n/cn/_posts/2023-11-16-frida-16-1-7-released.markdown create mode 100644 _i18n/cn/_posts/2023-11-28-frida-16-1-8-released.markdown create mode 100644 _i18n/cn/_posts/2023-12-20-frida-16-1-9-released.markdown create mode 100644 _i18n/cn/_posts/2023-12-24-frida-16-1-10-released.markdown create mode 100644 _i18n/cn/_posts/2024-01-12-frida-16-1-11-released.markdown create mode 100644 _i18n/cn/_posts/2024-02-16-frida-16-2-0-released.markdown create mode 100644 _i18n/cn/_posts/2024-02-16-frida-16-2-1-released.markdown create mode 100644 _i18n/cn/_posts/2024-05-17-frida-16-2-2-released.markdown create mode 100644 _i18n/cn/_posts/2024-05-18-frida-16-2-3-released.markdown create mode 100644 _i18n/cn/_posts/2024-05-20-frida-16-2-4-released.markdown create mode 100644 _i18n/cn/_posts/2024-05-21-frida-16-2-5-released.markdown create mode 100644 _i18n/cn/_posts/2024-05-31-frida-16-3-0-released.markdown create mode 100644 _i18n/cn/_posts/2024-05-31-frida-16-3-1-released.markdown create mode 100644 _i18n/cn/_posts/2024-06-04-frida-16-3-2-released.markdown create mode 100644 _i18n/cn/_posts/2024-06-04-frida-16-3-3-released.markdown create mode 100644 _i18n/cn/_posts/2024-07-05-frida-16-4-0-released.markdown create mode 100644 _i18n/cn/_posts/2024-07-06-frida-16-4-1-released.markdown create mode 100644 _i18n/cn/_posts/2024-07-08-frida-16-4-2-released.markdown create mode 100644 _i18n/cn/_posts/2024-07-13-frida-16-4-3-released.markdown create mode 100644 _i18n/cn/_posts/2024-07-16-frida-16-4-4-released.markdown create mode 100644 _i18n/cn/_posts/2024-07-17-frida-16-4-5-released.markdown create mode 100644 _i18n/cn/_posts/2024-07-22-frida-16-4-6-released.markdown create mode 100644 _i18n/cn/_posts/2024-07-23-frida-16-4-7-released.markdown create mode 100644 _i18n/cn/_posts/2024-08-02-frida-16-4-8-released.markdown create mode 100644 _i18n/cn/_posts/2024-08-20-frida-16-4-9-released.markdown create mode 100644 _i18n/cn/_posts/2024-08-22-frida-16-4-10-released.markdown create mode 100644 _i18n/cn/_posts/2024-09-06-frida-16-5-0-released.markdown create mode 100644 _i18n/cn/_posts/2024-09-06-frida-16-5-1.markdown create mode 100644 _i18n/cn/_posts/2024-09-19-frida-16-5-2.markdown create mode 100644 _i18n/cn/_posts/2024-10-10-frida-16-5-3.markdown create mode 100644 _i18n/cn/_posts/2024-10-10-frida-16-5-4.markdown create mode 100644 _i18n/cn/_posts/2024-10-11-frida-16-5-5.markdown create mode 100644 _i18n/cn/_posts/2024-10-14-frida-16-5-6.markdown create mode 100644 _i18n/cn/_posts/2024-11-14-frida-16-5-7.markdown create mode 100644 _i18n/cn/_posts/2024-12-09-frida-16-5-8-released.markdown create mode 100644 _i18n/cn/_posts/2024-12-09-frida-16-5-9-released.markdown create mode 100644 _i18n/cn/_posts/2025-01-09-frida-16-6-0-released.markdown create mode 100644 _i18n/cn/_posts/2025-01-10-frida-16-6-1-released.markdown create mode 100644 _i18n/cn/_posts/2025-01-13-frida-16-6-2-released.markdown create mode 100644 _i18n/cn/_posts/2025-01-14-frida-16-6-3-released.markdown create mode 100644 _i18n/cn/_posts/2025-01-17-frida-16-6-4-released.markdown create mode 100644 _i18n/cn/_posts/2025-01-23-frida-16-6-5-released.markdown create mode 100644 _i18n/cn/_posts/2025-01-27-frida-16-6-6-released.markdown create mode 100644 _i18n/cn/_posts/2025-03-13-frida-16-7-0-released.markdown create mode 100644 _i18n/cn/_posts/2025-03-21-frida-16-7-1-released.markdown create mode 100644 _i18n/cn/_posts/2025-03-21-frida-16-7-2-released.markdown create mode 100644 _i18n/cn/_posts/2025-03-21-frida-16-7-3-released.markdown create mode 100644 _i18n/cn/_posts/2025-03-31-frida-16-7-4-released.markdown create mode 100644 _i18n/cn/_posts/2025-04-05-frida-16-7-5-released.markdown create mode 100644 _i18n/cn/_posts/2025-04-05-frida-16-7-6-released.markdown create mode 100644 _i18n/cn/_posts/2025-04-05-frida-16-7-7-released.markdown create mode 100644 _i18n/cn/_posts/2025-04-07-frida-16-7-8-released.markdown create mode 100644 _i18n/cn/_posts/2025-04-07-frida-16-7-9-released.markdown create mode 100644 _i18n/cn/_posts/2025-04-09-frida-16-7-10-released.markdown create mode 100644 _i18n/cn/_posts/2025-04-14-frida-16-7-11-released.markdown create mode 100644 _i18n/cn/_posts/2025-04-16-frida-16-7-12-released.markdown create mode 100644 _i18n/cn/_posts/2025-04-17-frida-16-7-13-released.markdown create mode 100644 _i18n/cn/_posts/2025-04-25-frida-16-7-14-released.markdown create mode 100644 _i18n/cn/_posts/2025-05-08-frida-16-7-15-released.markdown create mode 100644 _i18n/cn/_posts/2025-05-08-frida-16-7-16-released.markdown create mode 100644 _i18n/cn/_posts/2025-05-09-frida-16-7-17-released.markdown create mode 100644 _i18n/cn/_posts/2025-05-09-frida-16-7-18-released.markdown create mode 100644 _i18n/cn/_posts/2025-05-14-frida-16-7-19-released.markdown create mode 100644 _i18n/cn/_posts/2025-05-17-frida-17-0-0-released.markdown create mode 100644 _i18n/cn/_posts/2025-05-17-frida-17-0-1-released.markdown create mode 100644 _i18n/cn/_posts/2025-05-20-frida-17-0-2-released.markdown create mode 100644 _i18n/cn/_posts/2025-05-20-frida-17-0-3-released.markdown create mode 100644 _i18n/cn/_posts/2025-05-22-frida-17-0-4-released.markdown create mode 100644 _i18n/cn/_posts/2025-05-24-frida-17-0-5-released.markdown create mode 100644 _i18n/cn/_posts/2025-05-28-frida-17-0-6-released.markdown create mode 100644 _i18n/cn/_posts/2025-05-29-frida-17-0-7-released.markdown create mode 100644 _i18n/cn/_posts/2025-06-05-frida-17-1-0-released.markdown create mode 100644 _i18n/cn/_posts/2025-06-06-frida-17-1-1-released.markdown create mode 100644 _i18n/cn/_posts/2025-06-06-frida-17-1-2-released.markdown create mode 100644 _i18n/cn/_posts/2025-06-07-frida-17-1-3-released.markdown create mode 100644 _i18n/cn/_posts/2025-06-10-frida-17-1-4-released.markdown create mode 100644 _i18n/cn/_posts/2025-06-13-frida-17-1-5-released.markdown create mode 100644 _i18n/cn/_posts/2025-06-18-frida-17-2-0-released.markdown create mode 100644 _i18n/cn/_posts/2025-06-19-frida-17-2-1-released.markdown create mode 100644 _i18n/cn/_posts/2025-06-19-frida-17-2-2-released.markdown create mode 100644 _i18n/cn/_posts/2025-06-20-frida-17-2-3-released.markdown create mode 100644 _i18n/cn/_posts/2025-06-20-frida-17-2-4-released.markdown create mode 100644 _i18n/cn/_posts/2025-06-23-frida-17-2-5-released.markdown create mode 100644 _i18n/cn/_posts/2025-06-27-frida-17-2-6-released.markdown create mode 100644 _i18n/cn/_posts/2025-07-01-frida-17-2-7-released.markdown create mode 100644 _i18n/cn/_posts/2025-07-02-frida-17-2-8-released.markdown create mode 100644 _i18n/cn/_posts/2025-07-02-frida-17-2-9-released.markdown create mode 100644 _i18n/cn/_posts/2025-07-03-frida-17-2-10-released.markdown create mode 100644 _i18n/cn/_posts/2025-07-03-frida-17-2-11-released.markdown create mode 100644 _i18n/cn/_posts/2025-07-18-frida-17-2-12-released.markdown create mode 100644 _i18n/cn/_posts/2025-07-21-frida-17-2-13-released.markdown create mode 100644 _i18n/cn/_posts/2025-07-24-frida-17-2-14-released.markdown create mode 100644 _i18n/cn/_posts/2025-08-02-frida-17-2-15-released.markdown create mode 100644 _i18n/cn/_posts/2025-08-12-frida-17-2-16-released.markdown create mode 100644 _i18n/cn/_posts/2025-08-20-frida-17-2-17-released.markdown create mode 100644 _i18n/cn/_posts/2025-09-15-frida-17-3-0-released.markdown create mode 100644 _i18n/cn/_posts/2025-09-18-frida-17-3-1-released.markdown create mode 100644 _i18n/cn/_posts/2025-09-19-frida-17-3-2-released.markdown create mode 100644 _i18n/cn/_posts/2025-10-12-frida-17-4-0-released.markdown create mode 100644 _i18n/cn/_posts/2025-10-24-frida-17-4-1-released.markdown create mode 100644 _i18n/cn/_posts/2025-10-29-frida-17-4-2-released.markdown create mode 100644 _i18n/cn/_posts/2025-10-31-frida-17-4-3-released.markdown create mode 100644 _i18n/cn/_posts/2025-11-01-frida-17-4-4-released.markdown create mode 100644 _i18n/cn/_posts/2025-11-04-frida-17-5-0-released.markdown create mode 100644 _i18n/cn/_posts/2025-11-05-frida-17-5-1-released.markdown diff --git a/_i18n/cn.yml b/_i18n/cn.yml new file mode 100644 index 00000000..ced8c34a --- /dev/null +++ b/_i18n/cn.yml @@ -0,0 +1,54 @@ +- title: 入门指南 + docs: + - home + - quickstart + - installation + - modes + - gadget + - hacking + - presentations + +- title: 教程 + docs: + - functions + - messages + - ios + - android + +- title: 示例 + docs: + - examples/windows + - examples/macos + - examples/linux + - examples/ios + - examples/android + - examples/javascript + +- title: 工具 + docs: + - frida-cli + - frida-ps + - frida-trace + - frida-discover + - frida-ls-devices + - frida-kill + +- title: API 参考 + docs: + - javascript-api + - c-api + - swift-api + +- title: 其他 + docs: + - best-practices + - troubleshooting + - building + - footprint + +- title: 关于 + docs: + - contributing + - gsoc-ideas-2015 + - gsod-ideas-2023 + - history diff --git a/_i18n/cn/_docs/android.md b/_i18n/cn/_docs/android.md new file mode 100644 index 00000000..f249bbef --- /dev/null +++ b/_i18n/cn/_docs/android.md @@ -0,0 +1,93 @@ +在本教程中,我们将展示如何在您的 Android 设备上进行函数跟踪。 + +## 设置您的 Android 设备 + +在开始之前,如果您还没有 root 您的设备,您需要先 root。从技术上讲,不 root 设备也可以使用 Frida,例如通过重新打包应用以包含 frida-gadget,或使用调试器来实现相同的目的。但是,对于本介绍,我们将专注于最简单的情况:已 root 的设备。 + +另请注意,我们最近的大部分测试都是在运行 Android 9 的 Pixel 3 上进行的。较旧的 ROM 可能也可以工作,但是如果您遇到基本问题,例如启动应用程序时 Frida 使系统崩溃,这是由于特定于 ROM 的怪癖。我们无法在所有可能的设备上进行测试,因此我们依靠您的帮助来改进这一点。但是,如果您刚开始使用 Frida,强烈建议使用运行最新官方软件的 Pixel 或 Nexus 设备,或者软件尽可能接近 AOSP 的设备。另一种选择是使用模拟器,最好使用 Google 提供的适用于 arm 或 arm64 的 Android 9 模拟器镜像。(x86 可能也可以工作,但测试明显较少。) + +您还需要 Android SDK 中的 `adb` 工具。 + +首先,从我们的 [releases 页面](https://github.com/frida/frida/releases)下载适用于 Android 的最新 `frida-server` 并解压缩。 + +{% highlight bash %} +$ adb shell getprop ro.product.cpu.abilist # check your device cpu type + +$ unxz frida-server.xz +{% endhighlight %} + +现在,让我们在您的设备上运行它: + +{% highlight bash %} +$ adb root # might be required +$ adb push frida-server /data/local/tmp/ +$ adb shell "chmod 755 /data/local/tmp/frida-server" +$ adb shell "/data/local/tmp/frida-server &" +{% endhighlight %} + +某些应用可能能够检测到 frida-server 的位置。将 frida-server 二进制文件重命名为随机名称,或将其移动到另一个位置(如 /dev)可能会奏效。 + +对于最后一步,请确保以 root 身份启动 frida-server,即如果您在已 root 的设备上执行此操作,您可能需要 *su* 并从该 shell 运行它。 + +
+
生产版本上的 adb
+

+ 如果在运行 adb root 后收到 adbd cannot run as root in production builds
您需要在每个 shell 命令前加上 su -c。例如: + adb shell "su -c chmod 755 /data/local/tmp/frida-server" +

+
+ +接下来,确保 `adb` 可以看到您的设备: + +{% highlight bash %} +$ adb devices -l +{% endhighlight %} + +这也将确保 adb 守护进程在您的桌面上运行,这允许 Frida 发现并与您的设备通信,无论您是通过 USB 还是 WiFi 连接它。 + +## 快速冒烟测试 + +现在,在您的桌面上,是时候确保基础功能正常工作了。运行: + +{% highlight bash %} +$ frida-ps -U +{% endhighlight %} + +这应该会给您一个类似于以下的进程列表: + +{% highlight bash %} + PID NAME + 1590 com.facebook.katana +13194 com.facebook.katana:providers +12326 com.facebook.orca +13282 com.twitter.android +… +{% endhighlight %} + +太棒了,我们可以开始了! + +## 跟踪 Chrome 中的 open() 调用 + +好了,让我们找点乐子。在您的设备上启动 Chrome 应用,然后返回桌面并运行: + +{% highlight bash %} +$ frida-trace -U -i open -N com.android.chrome +Uploading data... +open: Auto-generated handler …/linker/open.js +open: Auto-generated handler …/libc.so/open.js +Started tracing 2 functions. Press Ctrl+C to stop. +{% endhighlight %} + +现在只需玩一下 Chrome 应用,您应该开始看到 `open()` 调用飞入: + +{% highlight bash %} +1392 ms open() +1403 ms open() +1420 ms open() +{% endhighlight %} + +您现在可以在阅读 `man open` 时实时编辑上述 JavaScript 文件,并开始越来越深入地研究您的 Android 应用。 + +## 构建您自己的工具 + +虽然像 *frida*、*frida-trace* 等 CLI 工具绝对非常有用,但有时您可能希望利用强大的 [Frida API](/docs/javascript-api/) 构建自己的工具。为此,我们建议阅读有关 [Functions](/docs/functions) 和 [Messages](/docs/messages) 的章节,并且在任何看到 `frida.attach()` 的地方,只需将其替换为 `frida.get_usb_device().attach()`。 diff --git a/_i18n/cn/_docs/best-practices.md b/_i18n/cn/_docs/best-practices.md new file mode 100644 index 00000000..73dd09d6 --- /dev/null +++ b/_i18n/cn/_docs/best-practices.md @@ -0,0 +1,71 @@ +本节旨在包含使用 Frida 时经常遇到的最佳实践和陷阱。 + +### 字符串分配 (UTF-8/UTF-16/ANSI) + +阅读文档后,人们可能会认为分配/替换字符串就像这样简单: + +{% highlight javascript %} +onEnter(args) { + args[0].writeUtf8String('mystring'); +} +{% endhighlight %} + +但是,这可能是不可能的,因为指向的字符串可能: + +- 驻留在“只读数据”段中,该段作为只读映射到进程的地址空间; +- 比那里的字符串长,因此 *writeUtf8String()* 会导致缓冲区溢出并可能破坏不相关的内存。 + +即使您可以通过使用 *Memory.protect()* 解决前一个问题,也有一个更好的解决方案:分配一个新字符串并替换参数。 + +但是有一个陷阱:*Memory.allocUtf8String()* 返回的值必须保持活动状态——一旦 JavaScript 值被垃圾回收,它就会被释放。这意味着它至少需要在函数调用期间保持活动状态,在某些情况下甚至更长;确切的语义取决于 API 的设计方式。 + +考虑到这一点,执行此操作的可靠方法是: + +{% highlight javascript %} +onEnter(args) { + const buf = Memory.allocUtf8String('mystring'); + this.buf = buf; + args[0] = buf; +} +{% endhighlight %} + +它的工作原理是 *this* 绑定到一个每个线程和每个调用都存在的对象,您存储在那里的任何东西都将在 *onLeave* 中可用,这甚至在递归的情况下也有效。这样您就可以在 *onEnter* 中读取参数,并在稍后的 *onLeave* 中访问它们。这也是在函数调用期间保持内存分配处于活动状态的推荐方法。 + +如果函数保留指针并在函数调用完成后也使用它,一种解决方案是像这样操作: + +{% highlight javascript %} +const myStringBuf = Memory.allocUtf8String('mystring'); + +Interceptor.attach(f, { + onEnter(args) { + args[0] = myStringBuf; + } +}); +{% endhighlight %} + +### 重用参数 + +在 *onEnter* 回调中读取参数时,通常通过索引访问每个参数。但是当多次访问同一个参数时会发生什么?以这段代码为例: + +{% highlight javascript %} +Interceptor.attach(f, { + onEnter(args) { + if (!args[0].readUtf8String(4).includes('MZ')) { + console.log(hexdump(args[0])); + } + } +}); +{% endhighlight %} + +在上面的示例中,第一个参数从 *args* 数组中获取了两次,这就支付了两次向 *frida-gum* 查询此信息的成本。为了避免在多次需要相同参数时浪费宝贵的 CPU 周期,最好使用局部变量存储此信息: + +{% highlight javascript %} +Interceptor.attach(f, { + onEnter(args) { + const firstArg = args[0]; + if (!firstArg.readUtf8String(4).includes('MZ')) { + console.log(hexdump(firstArg)); + } + } +}); +{% endhighlight %} diff --git a/_i18n/cn/_docs/bridges.md b/_i18n/cn/_docs/bridges.md new file mode 100644 index 00000000..376d3d11 --- /dev/null +++ b/_i18n/cn/_docs/bridges.md @@ -0,0 +1,176 @@ +从 Frida 17.0.0 开始,桥接器不再与 Frida 的 GumJS 运行时捆绑在一起。您可以在 [release notes][] 中阅读更多相关信息。这意味着用户现在必须显式引入他们想要使用的桥接器。不过,Frida REPL 和 `frida-trace` 确实捆绑了所有三个桥接器,以便与现有脚本兼容。 + +## 目录 + +1. **REPL 和 frida-trace** + 1. [使用纯 JavaScript](#使用纯-javascript) + 1. [REPL 自动编译](#repl-自动编译) + 1. [使用 frida-compile 手动编译](#使用-frida-compile-手动编译) +1. **使用 API** + 1. [Python 示例](#python-示例) + 1. [Go 示例](#go-示例) + +## REPL 和 frida-trace + +我们将使用一个简单的脚本将 `ObjC.available` 打印到屏幕上。 + +{% highlight javascript %} +// script.js +console.log(ObjC.available); +{% endhighlight %} + +### 使用纯 JavaScript + +这与以前完全一样,因为 REPL 和 frida-trace 捆绑了所有三个桥接器。 + +{% highlight bash %} +$ frida -p0 -l script.js + ____ + / _ | Frida 17.0.5 - A world-class dynamic instrumentation toolkit + | (_| | + > _ | Commands: + /_/ |_| help -> Displays the help system + . . . . object? -> Display information about 'object' + . . . . exit/quit -> Exit + . . . . + . . . . More info at https://frida.re/docs/home/ + . . . . + . . . . Connected to Local System (id=local) +Attaching... +true +[Local::SystemSession ]-> +{% endhighlight %} + +### REPL 自动编译 + +REPL 也可以使用 `.ts` 文件:在空目录中使用 `frida-create -t agent` 来设置所需的脚手架。 + +### 使用 frida-compile 手动编译 + +您需要通过在脚本中添加行来指定要使用的桥接器(ObjC, Java, Swift): + +* `import ObjC from "frida-objc-bridge";` - 用于 ObjC +* `import Swift from "frida-swift-bridge";` - 用于 Swift +* `import Java from "frida-java-bridge";` - 用于 Java + +我们将重新创建上面的示例,其中我们使用纯 JavaScript 打印 `ObjC.available`。 + +{% highlight typescript %} +// script.ts +import ObjC from "frida-objc-bridge"; + +console.log(ObjC.available); +{% endhighlight %} + +在空目录中初始化并安装必要的包: + +{% highlight bash %} +$ frida-create -t agent +$ npm install +$ npm install frida-objc-bridge +{% endhighlight %} + +然后编译代理并加载它: + +{% highlight bash %} +$ frida-compile script.ts -o _agent.js -S -c +$ frida -p0 -l _agent.js + ____ + / _ | Frida 17.0.5 - A world-class dynamic instrumentation toolkit + | (_| | + > _ | Commands: + /_/ |_| help -> Displays the help system + . . . . object? -> Display information about 'object' + . . . . exit/quit -> Exit + . . . . + . . . . More info at https://frida.re/docs/home/ + . . . . + . . . . Connected to Local System (id=local) +Attaching... +true +[Local::SystemSession ]-> +{% endhighlight %} + +## 使用 API + +使用绑定提供的 API 需要几个步骤,即: + +* 编写脚本 +* 运行 `frida-create -t agent` +* 安装您想要的桥接器,例如 `frida-objc-bridge` +* 编写代码来编译脚本并将其加载到进程中 + +### Python 示例 + +{% highlight python %} +import frida + +def on_diagnostics(diag): + print("diag", diag) + +def on_message(message, data): + print(message) + +compiler = frida.Compiler() +compiler.on("diagnostics", on_diagnostics) +# script is located in /tmp, so we set project root to /tmp +bundle = compiler.build("script.ts", project_root="/tmp") + +session = frida.attach(0) + +script = session.create_script(bundle) + +script.on("message", on_message) +script.load() +{% endhighlight %} + +### Go 示例 + +{% highlight go %} +package main + +import ( + "bufio" + "fmt" + "github.com/frida/frida-go/frida" + "os" +) + +func main() { + comp := frida.NewCompiler() + comp.On("diagnostics", func(diag string) { + fmt.Printf("Diagnostics: %s\n", diag) + }) + + bopts := frida.NewCompilerOptions() + bopts.SetProjectRoot("/tmp") + bopts.SetSourceMaps(frida.SourceMapsOmitted) + bopts.SetJSCompression(frida.JSCompressionTerser) + + bundle, err := comp.Build("script.ts", bopts) + if err != nil { + panic(err) + } + + session, err := frida.Attach(0) + if err != nil { + panic(err) + } + + script, err := session.CreateScript(bundle) + if err != nil { + panic(err) + } + + script.On("message", func(message string, data []byte) { + fmt.Printf("%s\n", message) + }) + + script.Load() + + r := bufio.NewReader(os.Stdin) + r.ReadLine() +} +{% endhighlight %} + +[release notes]: /news/2025/05/17/frida-17-0-0-released/ diff --git a/_i18n/cn/_docs/building.md b/_i18n/cn/_docs/building.md new file mode 100644 index 00000000..545ff6f3 --- /dev/null +++ b/_i18n/cn/_docs/building.md @@ -0,0 +1,100 @@ +## 目录 + +1. 构建 Frida + - [先决条件](#先决条件) + - [获取代码](#获取代码) + - [为本机构建](#为本机构建) + - [为不同机器构建](#为不同机器构建) + - [树外构建](#树外构建) + +## 构建 Frida + +### 先决条件 + +你需要: + +- C/C++ 工具链 +- Node.js >= 18 +- Git + +例如在 Ubuntu 系统上: + +{% highlight bash %} +$ sudo apt-get install build-essential git lib32stdc++-9-dev \ + libc6-dev-i386 nodejs npm +{% endhighlight %} + +### 获取代码 + +{% highlight bash %} +$ git clone https://github.com/frida/frida.git +{% endhighlight %} + +### 为本机构建 + +要构建,请运行: + +{% highlight bash %} +$ make +{% endhighlight %} + +这将使用 `./build` 作为构建目录。运行 `make install` 进行安装。 + +您也可以先运行 `./configure` 来指定 `--prefix` 或任何其他选项。使用 `--help` 列出顶级选项。 + +要设置较低级别的选项,请执行: + +{% highlight bash %} +$ ./configure -- first-option second-option … +{% endhighlight %} + +`--` 之后的选项直接传递给 Meson 的 `setup` 命令。这意味着您也可以将项目选项传递给子项目,例如: + +{% highlight bash %} +$ ./configure -- \ + -Dfrida-gum:devkits=gum,gumjs \ + -Dfrida-core:devkits=core +{% endhighlight %} + +有关可用选项,请查阅 subprojects/* 中的 `meson.options`。您也可以单独克隆不同的仓库,并按照此处描述的相同方式进行构建。 + +### 为不同机器构建 + +#### iOS/watchOS/tvOS + +{% highlight bash %} +$ ./configure --host=ios-arm64 +# or: ./configure --host=watchos-arm64 +# or: ./configure --host=tvos-arm64 +# optionally suffixed by `-simulator` +$ make +{% endhighlight %} + +#### Android + +{% highlight bash %} +$ ./configure --host=android-arm64 +$ make +{% endhighlight %} + +#### Raspberry Pi + +{% highlight bash %} +$ sudo apt-get install g++-arm-linux-gnueabihf +$ ./configure --host=arm-linux-gnueabihf +$ make +{% endhighlight %} + +### 树外构建 + +有时您可能希望使用单个源代码树为多个系统或配置进行构建。为此,请从源代码树外部的空目录调用 `configure`: + +{% highlight bash %} +$ mkdir build-ios +$ ../frida/configure --host=ios-arm64 +$ make +$ cd .. +$ mkdir build-android +$ ../frida/configure --host=android-arm64 +$ make +{% endhighlight %} diff --git a/_i18n/cn/_docs/c-api.md b/_i18n/cn/_docs/c-api.md new file mode 100644 index 00000000..2deacd45 --- /dev/null +++ b/_i18n/cn/_docs/c-api.md @@ -0,0 +1,33 @@ +## 入门 + +为您提供用于注入、函数操作、内存读取等的 JavaScript API 的功能也可从 C 获得。 + +Frida 分为几个模块,我们将在下面讨论。 + +这些模块都可以单独编译,也可以在 [releases 页面](https://github.com/frida/frida/releases)上找到。 + +devkit 下载附带了如何使用每个模块的示例。使用 devkit 是学习如何利用每个模块的最佳方式。 + +## core + +frida-core 包含主要的注入代码。通过 frida-core,您可以注入进程、创建运行 QuickJS 的线程并运行您的 JavaScript。 + +有关源代码,请参阅 [frida-core](https://github.com/frida/frida-core) 仓库。 + +## gum + +frida-gum 允许您使用 C 增强和替换函数。 + +devkit 中的示例向您展示了如何仅使用 C 增强 `open` 和 `close`。 + +有关源代码,请参阅 [frida-gum](https://github.com/frida/frida-gum) 仓库。 + +## gumjs + +frida-gumjs 包含 JavaScript 绑定。 + +## gadget + +类似于 frida-agent,除了通过 DYLD_INSERT_LIBRARIES 注入、与应用程序捆绑等,它可以在远程模式下运行,在该模式下它监听并且看起来就像 frida-server。 + +_请点击上面的“Improve this page”并添加示例。谢谢!_ diff --git a/_i18n/cn/_docs/contributing.md b/_i18n/cn/_docs/contributing.md new file mode 100644 index 00000000..3d88886f --- /dev/null +++ b/_i18n/cn/_docs/contributing.md @@ -0,0 +1,18 @@ +Frida 阿姨需要你! + +如果你能做以下事情,你会让她非常高兴: + +- 发现错误时报告错误 +- 编写更多甚至更好的文档 +- 制作我们在新闻中听到的那些很棒的 YouTube 教程视频 +- 用 Frida 构建一些很酷的应用程序 +- 帮忙制作一些新的、花哨的网页,让我们看起来都很棒 +- 将新功能贡献回 Frida 代码库 + +无论如何,请加入我们的邮件列表: + +[https://groups.google.com/d/forum/frida-dev](https://groups.google.com/d/forum/frida-dev) + +也不要忘记加入我们的 IRC 频道: + +[irc.freenode.net/#frida](irc://irc.freenode.net/#frida) diff --git a/_i18n/cn/_docs/examples/android.md b/_i18n/cn/_docs/examples/android.md new file mode 100644 index 00000000..44cac3ef --- /dev/null +++ b/_i18n/cn/_docs/examples/android.md @@ -0,0 +1,107 @@ +## 为 Android CTF 构建的示例工具 + +对于此特定示例,强烈建议使用 Android 4.4 x86 模拟器镜像。此工具基于 SECCON Quals CTF 2015 APK1 示例,[在此处](https://github.com/ctfs/write-ups-2015/tree/master/seccon-quals-ctf-2015/binary/reverse-engineering-android-apk-1)下载 APK。 + +将代码保存为 *ctf.py* 并以 `python ctf.py` 运行。 + +{% highlight py %} +import frida, sys + +def on_message(message, data): + if message['type'] == 'send': + print("[*] {0}".format(message['payload'])) + else: + print(message) + +jscode = """ +Java.perform(() => { + // Function to hook is defined here + const MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity'); + + // Whenever button is clicked + const onClick = MainActivity.onClick; + onClick.implementation = function (v) { + // Show a message to know that the function got called + send('onClick'); + + // Call the original onClick handler + onClick.call(this, v); + + // Set our values after running the original onClick handler + this.m.value = 0; + this.n.value = 1; + this.cnt.value = 999; + + // Log to the console that it's done, and we should have the flag! + console.log('Done:' + JSON.stringify(this.cnt)); + }; +}); +""" + +process = frida.get_usb_device().attach('com.example.seccon2015.rock_paper_scissors') +script = process.create_script(jscode) +script.on('message', on_message) +print('[*] Running CTF') +script.load() +sys.stdin.read() +{% endhighlight %} + +注意我们使用 `this.m.value = 0` 而不是 `this.m = 0` 来设置字段的值。 +如果此类中还有一个名为 `m` 的方法,我们需要使用 `this._m.value = 0` 来设置字段 `m` 的值。通常,在查看对象的属性时,必须使用 `.value` 来访问这些字段引用的值。 + + +## 使用 Java bridge 可以做什么的示例 + +Frida 中 Java bridge 的一些可能性: + +{% highlight js %} +Java.perform(() => { + // Create an instance of java.lang.String and initialize it with a string + const JavaString = Java.use('java.lang.String'); + const exampleString1 = JavaString.$new('Hello World, this is an example string in Java.'); + console.log('[+] exampleString1: ' + exampleString1); + console.log('[+] exampleString1.length(): ' + exampleString1.length()); + + // Create an instance of java.nio.charset.Charset, and initialize the default character set + const Charset = Java.use('java.nio.charset.Charset'); + const charset = Charset.defaultCharset(); + // Create a byte array of a Javascript string + const charArray = 'This is a Javascript string converted to a byte array.'.split('').map(c => c.charCodeAt(0)); + + // Create an instance of java.lang.String and initialize it through an overloaded $new, + // with a byte array and a instance of java.nio.charset.Charset + const exampleString2 = JavaString.$new.overload('[B', 'java.nio.charset.Charset').call(JavaString, charArray, charset) + console.log('[+] exampleString2: ' + exampleString2); + console.log('[+] exampleString2.length(): ' + exampleString2.length()); + + // Intercept the initialization of java.lang.Stringbuilder's overloaded constructor, + // and write the partial argument to the console + const StringBuilder = Java.use('java.lang.StringBuilder'); + // We need to replace .$init() instead of .$new(), since .$new() = .alloc() + .init() + const ctor = StringBuilder.$init.overload('java.lang.String'); + ctor.implementation = function (arg) { + let partial = ''; + const result = ctor.call(this, arg); + if (arg !== null) { + partial = arg.toString().replace('\n', '').slice(0, 10); + } + // console.log('new StringBuilder(java.lang.String); => ' + result); + console.log('new StringBuilder("' + partial + '");'); + return result; + }; + console.log('[+] new StringBuilder(java.lang.String) hooked'); + + // Intercept the toString() method of java.lang.StringBuilder and write its partial contents to the console. + const toString = StringBuilder.toString; + toString.implementation = function () { + const result = toString.call(this); + let partial = ''; + if (result !== null) { + partial = result.toString().replace('\n', '').slice(0, 10); + } + console.log('StringBuilder.toString(); => ' + partial); + return result; + }; + console.log('[+] StringBuilder.toString() hooked'); +}); +{% endhighlight %} diff --git a/_i18n/cn/_docs/examples/ios.md b/_i18n/cn/_docs/examples/ios.md new file mode 100644 index 00000000..88ddc31f --- /dev/null +++ b/_i18n/cn/_docs/examples/ios.md @@ -0,0 +1,142 @@ +Frida 通过将 `:` 替换为 `_` 来提供 Objective-C 选择器的包装函数: + +{% highlight js %} +// +[NSJSONSerialization dataWithJSONObject:options:error:] +ObjC.classes.NSJSONSerialization.dataWithJSONObject_options_error_(...) + +// NSString *helloWorldString = @"Hello, World!"; +var helloWorldString = ObjC.classes.NSString.stringWithString_("Hello, World!"); + +// [helloWorldString characterAtIndex:0] +helloWorldString.characterAtIndex_(0) +{% endhighlight %} + +>**提示**:如果事情似乎没有按预期工作,您可能正在与错误的数据类型交互 - 运行以下命令以确定您正在处理的对象的实际类型! + +{% highlight js %} +console.log('Type of args[2] -> ' + new ObjC.Object(args[2]).$className) +{% endhighlight %} + +## 将 NSData 转换为 String + +{% highlight js %} +const data = new ObjC.Object(args[2]); +data.bytes().readUtf8String(data.length()); +{% endhighlight %} + + >**提示**:如果字符串数据以 null 结尾,则不需要第 2 个参数(字节数)。 + +## 将 NSData 转换为二进制数据 + +{% highlight js %} +const data = new ObjC.Object(args[2]); +data.bytes().readByteArray(data.length()); +{% endhighlight %} + +## 迭代 NSArray + +{% highlight js %} +const array = new ObjC.Object(args[2]); +/* + * Be sure to use valueOf() as NSUInteger is a Number in + * 32-bit processes, and UInt64 in 64-bit processes. This + * coerces it into a Number in the latter case. + */ +const count = array.count().valueOf(); +for (let i = 0; i !== count; i++) { + const element = array.objectAtIndex_(i); +} +{% endhighlight %} + +## 迭代 NSDictionary + +{% highlight js %} +const dict = new ObjC.Object(args[2]); +const enumerator = dict.keyEnumerator(); +let key; +while ((key = enumerator.nextObject()) !== null) { + const value = dict.objectForKey_(key); +} +{% endhighlight %} + +## 解档 NSKeyedArchiver + +{% highlight js %} +const parsedValue = ObjC.classes.NSKeyedUnarchiver.unarchiveObjectWithData_(value); +{% endhighlight %} + +## 读取结构体 + +如果 args[0] 是指向结构体的指针,假设您想读取偏移量 4 处的 uint32,您可以如下所示进行操作: +{% highlight js %} +args[0].add(4).readU32(); +{% endhighlight %} + +## 在 iOS 7 上显示警报框 + +{% highlight js %} +const UIAlertView = ObjC.classes.UIAlertView; /* iOS 7 */ +const view = UIAlertView.alloc().initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles_( + 'Frida', + 'Hello from Frida', + NULL, + 'OK', + NULL); +view.show(); +view.release(); +{% endhighlight %} + +## 在 iOS >= 8 上显示警报框 + +这是以下 [代码](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIAlertController_class/) 的实现。 + +{% highlight js %} +// Defining a Block that will be passed as handler parameter to +[UIAlertAction actionWithTitle:style:handler:] +const handler = new ObjC.Block({ + retType: 'void', + argTypes: ['object'], + implementation() { + } +}); + +// Import ObjC classes +const UIAlertController = ObjC.classes.UIAlertController; +const UIAlertAction = ObjC.classes.UIAlertAction; +const UIApplication = ObjC.classes.UIApplication; + +// Using Grand Central Dispatch to pass messages (invoke methods) in application's main thread +ObjC.schedule(ObjC.mainQueue, () => { + // Using integer numerals for preferredStyle which is of type enum UIAlertControllerStyle + const alert = UIAlertController.alertControllerWithTitle_message_preferredStyle_('Frida', 'Hello from Frida', 1); + // Again using integer numeral for style parameter that is enum + const 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); +}); +{% endhighlight %} + +## 打印 NSURL 参数 + +以下代码显示了如何拦截对 [UIApplication openURL:] 的调用并显示传递的 NSURL。 + +{% highlight js %} +// Get a reference to the openURL selector +const openURL = ObjC.classes.UIApplication['- openURL:']; + +// Intercept the method +Interceptor.attach(openURL.implementation, { + onEnter(args) { + // As this is an Objective-C method, the arguments are as follows: + // 0. 'self' + // 1. The selector (openURL:) + // 2. The first argument to the openURL method + const myNSURL = new ObjC.Object(args[2]); + // Convert it to a JS string + const myJSURL = myNSURL.absoluteString().toString(); + // Log it + console.log('Launching URL: ' + myJSURL); + } +}); +{% endhighlight %} diff --git a/_i18n/cn/_docs/examples/javascript.md b/_i18n/cn/_docs/examples/javascript.md new file mode 100644 index 00000000..facfbba3 --- /dev/null +++ b/_i18n/cn/_docs/examples/javascript.md @@ -0,0 +1,105 @@ +## 连接到 Node.js 进程的 V8 VM 以注入任意 JS + +{% highlight js %} +const uv_default_loop = new NativeFunction(Module.getGlobalExportByName('uv_default_loop'), 'pointer', []); +const uv_async_init = new NativeFunction(Module.getGlobalExportByName('uv_async_init'), 'int', ['pointer', 'pointer', 'pointer']); +const uv_async_send = new NativeFunction(Module.getGlobalExportByName('uv_async_send'), 'int', ['pointer']); +const uv_close = new NativeFunction(Module.getGlobalExportByName('uv_close'), 'void', ['pointer', 'pointer']); +const uv_unref = new NativeFunction(Module.getGlobalExportByName('uv_unref'), 'void', ['pointer']); + +const v8_Isolate_GetCurrent = new NativeFunction(Module.getGlobalExportByName('_ZN2v87Isolate10GetCurrentEv'), 'pointer', []); +const v8_Isolate_GetCurrentContext = new NativeFunction(Module.getGlobalExportByName('_ZN2v87Isolate17GetCurrentContextEv'), 'pointer', ['pointer']); + +const v8_HandleScope_init = new NativeFunction(Module.getGlobalExportByName('_ZN2v811HandleScopeC1EPNS_7IsolateE'), 'void', ['pointer', 'pointer']); +const v8_HandleScope_finalize = new NativeFunction(Module.getGlobalExportByName('_ZN2v811HandleScopeD1Ev'), 'void', ['pointer']); + +const v8_String_NewFromUtf8 = new NativeFunction(Module.getGlobalExportByName('_ZN2v86String11NewFromUtf8EPNS_7IsolateEPKcNS_13NewStringTypeEi'), 'pointer', ['pointer', 'pointer', 'int', 'int']); + +const v8_Script_Compile = new NativeFunction(Module.getGlobalExportByName('_ZN2v86Script7CompileENS_5LocalINS_7ContextEEENS1_INS_6StringEEEPNS_12ScriptOriginE'), 'pointer', ['pointer', 'pointer', 'pointer']); +const v8_Script_Run = new NativeFunction(Module.getGlobalExportByName('_ZN2v86Script3RunENS_5LocalINS_7ContextEEE'), 'pointer', ['pointer', 'pointer']); + +const NewStringType = { + kNormal: 0, + kInternalized: 1 +}; + +const pending = []; + +const processPending = new NativeCallback(function () { + const isolate = v8_Isolate_GetCurrent(); + + const scope = Memory.alloc(24); + v8_HandleScope_init(scope, isolate); + + const context = v8_Isolate_GetCurrentContext(isolate); + + while (pending.length > 0) { + const item = pending.shift(); + const source = v8_String_NewFromUtf8(isolate, Memory.allocUtf8String(item), NewStringType.kNormal, -1); + const script = v8_Script_Compile(context, source, NULL); + const result = v8_Script_Run(script, context); + } + + v8_HandleScope_finalize(scope); +}, 'void', ['pointer']); + +const onClose = new NativeCallback(function () { + Script.unpin(); +}, 'void', ['pointer']); + +const handle = Memory.alloc(128); +uv_async_init(uv_default_loop(), handle, processPending); +uv_unref(handle); + +Script.bindWeak(handle, () => { + Script.pin(); + uv_close(handle, onClose); +}); + +function run(source) { + pending.push(source); + uv_async_send(handle); +} + +run('console.log("Hello from Frida");'); +{% endhighlight %} + +## 跟踪 Perl 5 进程中的函数调用 + +{% highlight js %} +const pointerSize = Process.pointerSize; +const SV_OFFSET_FLAGS = pointerSize + 4; +const PVGV_OFFSET_NAMEHEK = 4 * pointerSize; + +const SVt_PVGV = 9; + +Interceptor.attach(Module.getExportByName(null, 'Perl_pp_entersub'), { + onEnter(args) { + const interpreter = args[0]; + const stack = interpreter.readPointer(); + + const sub = stack.readPointer(); + + const flags = sub.add(SV_OFFSET_FLAGS).readU32(); + const type = flags & 0xff; + if (type === SVt_PVGV) { + /* + * Note: this console.log() is not ideal performance-wise, + * a proper implementation would buffer and submit events + * periodically with send(). + */ + console.log(GvNAME(sub) + '()'); + } else { + // XXX: Do we need to handle other types? + } + } +}); + +function GvNAME(sv) { + const body = sv.readPointer(); + const nameHek = body.add(PVGV_OFFSET_NAMEHEK).readPointer(); + return nameHek.add(8).readUtf8String(); +} +{% endhighlight %} + +_请点击上面的“Improve this page”并添加示例。谢谢!_ diff --git a/_i18n/cn/_docs/examples/linux.md b/_i18n/cn/_docs/examples/linux.md new file mode 100644 index 00000000..cda7fa87 --- /dev/null +++ b/_i18n/cn/_docs/examples/linux.md @@ -0,0 +1,6 @@ +# Docker 上的 Linux +为了在 Docker 中使用 Linux 运行 Frida,您需要在没有 seccomp 的情况下启动容器,例如: +``` +docker run --security-opt seccomp:unconfined -it /bin/bash +``` +上面的命令将创建一个基于您指定的镜像的容器,禁用 seccomp,并运行一个交互式 shell。然后您可以使用 `frida-trace` 来测试 Frida。 diff --git a/_i18n/cn/_docs/examples/macos.md b/_i18n/cn/_docs/examples/macos.md new file mode 100644 index 00000000..aee8f00a --- /dev/null +++ b/_i18n/cn/_docs/examples/macos.md @@ -0,0 +1,44 @@ +要为 macOS 设置 Frida,您需要授权 Frida 使用 task_for_pid 来访问您的目标进程。 + +如果您通过 GUI 使用本地用户运行 Frida 工具(例如从 Terminal.app),系统将通过 taskgate 提示您授权该进程。 + +您可能还需要禁用 [系统完整性保护](https://support.apple.com/en-us/HT204899)。 + +### Objective-C 基础 + +{% highlight py %} +import frida +import sys + +def on_message(message, data): + print("[{}] => {}".format(message, data)) + +def main(target_process): + session = frida.attach(target_process) + + script = session.create_script(""" + const appWillFinishLaunching = ObjC.classes.NSApplicationDelegate['- applicationWillFinishLaunching:']; + Interceptor.attach(appWillFinishLaunching.implementation, { + onEnter(args) { + // As this is an Objective-C method, the arguments are as follows: + // 0. 'self' + // 1. The selector (applicationWillFinishLaunching:) + // 2. The first argument to this method + const notification = new ObjC.Object(args[2]); + + // Convert it to a JS string and log it + const notificationStr = notification.absoluteString().toString(); + console.log('Will finish launching with notification: ' + notificationStr); + } + }); + """) + script.on("message", on_message) + script.load() + print("[!] Ctrl+D or Ctrl+Z to detach from instrumented program.\n\n") + sys.stdin.read() + session.detach() + + +if __name__ == "__main__": + main("Safari") +{% endhighlight %} diff --git a/_i18n/cn/_docs/examples/windows.md b/_i18n/cn/_docs/examples/windows.md new file mode 100644 index 00000000..bb8deac2 --- /dev/null +++ b/_i18n/cn/_docs/examples/windows.md @@ -0,0 +1,82 @@ +## 用于直接监视 jvm.dll 的示例工具 + +展示如何使用 Frida 监视由名为 *fledge.exe* (BB Simulator) 的进程执行的 jvm.dll。 + +将此代码保存为 *bb.py*,运行 BB Simulator (fledge.exe),然后运行 `python.exe bb.py fledge.exe` 以监视 *jvm.dll* 的 [AES](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) 使用情况。 + +{% highlight py %} +import frida +import sys + +def on_message(message, data): + print("[%s] => %s" % (message, data)) + +def main(target_process): + session = frida.attach(target_process) + + script = session.create_script(""" + + // Find base address of current imported jvm.dll by main process fledge.exe + const baseAddr = Process.getModuleByName('Jvm.dll').base; + console.log('Jvm.dll baseAddr: ' + baseAddr); + + const setAesDecrypt0 = resolveAddress('0x1FF44870'); // Here we use the function address as seen in our disassembler + + Interceptor.attach(setAesDecrypt0, { // Intercept calls to our SetAesDecrypt function + + // When function is called, print out its parameters + onEnter(args) { + console.log(''); + console.log('[+] Called SetAesDeCrypt0' + setAesDecrypt0); + console.log('[+] Ctx: ' + args[0]); + console.log('[+] Input: ' + args[1]); // Plaintext + console.log('[+] Output: ' + args[2]); // This pointer will store the de/encrypted data + console.log('[+] Len: ' + args[3]); // Length of data to en/decrypt + dumpAddr('Input', args[1], args[3].toInt32()); + this.outptr = args[2]; // Store arg2 and arg3 in order to see when we leave the function + this.outsize = args[3].toInt32(); + }, + + // When function is finished + onLeave(retval) { + dumpAddr('Output', this.outptr, this.outsize); // Print out data array, which will contain de/encrypted data as output + console.log('[+] Returned from setAesDecrypt0: ' + retval); + } + }); + + function dumpAddr(info, addr, size) { + if (addr.isNull()) + return; + + console.log('Data dump ' + info + ' :'); + const buf = addr.readByteArray(size); + + // If you want color magic, set ansi to true + console.log(hexdump(buf, { offset: 0, length: size, header: true, ansi: false })); + } + + function resolveAddress(addr) { + const idaBase = ptr('0x1FEE0000'); // Enter the base address of jvm.dll as seen in your favorite disassembler (here IDA) + const offset = ptr(addr).sub(idaBase); // Calculate offset in memory from base address in IDA database + const result = baseAddr.add(offset); // Add current memory base address to offset of function to monitor + console.log('[+] New addr=' + result); // Write location of function in memory to console + return result; + } +""") + script.on('message', on_message) + script.load() + print("[!] Ctrl+D on UNIX, Ctrl+Z on Windows/cmd.exe to detach from instrumented program.\n\n") + sys.stdin.read() + session.detach() + +if __name__ == '__main__': + if len(sys.argv) != 2: + print("Usage: %s " % __file__) + sys.exit(1) + + try: + target_process = int(sys.argv[1]) + except ValueError: + target_process = sys.argv[1] + main(target_process) +{% endhighlight %} diff --git a/_i18n/cn/_docs/footprint.md b/_i18n/cn/_docs/footprint.md new file mode 100644 index 00000000..fcde6f00 --- /dev/null +++ b/_i18n/cn/_docs/footprint.md @@ -0,0 +1,93 @@ +我们投入了大量精力来确保 Frida 能够从桌面一直扩展到嵌入式系统。 + +虽然我们的预构建二进制文件启用了所有功能,但自己构建 Frida 意味着您可以根据需要对其进行定制并构建明显更小的二进制文件。这是通过调整 `config.mk` 中的可用选项来完成的: + +{% highlight make %} +# Features ordered by binary footprint, from largest to smallest +FRIDA_V8 ?= enabled +FRIDA_CONNECTIVITY ?= enabled +FRIDA_DATABASE ?= enabled +FRIDA_JAVA_BRIDGE ?= auto +FRIDA_OBJC_BRIDGE ?= auto +FRIDA_SWIFT_BRIDGE ?= auto +{% endhighlight %} + +如果在嵌入式系统上工作,可以禁用所有上述功能。 + +具体来说,仅在以下情况下才需要它们: +- FRIDA_V8: 默认的 Javascript 运行时是 QuickJS,因此如果不使用它,可以安全地禁用它。如果需要 V8 运行时,例如通过 API `create_script(..., runtime='v8')` 特别请求或通过带有 `--runtime=v8` 的 frida-tools CLI 请求时,则需要它。 +- FRIDA_CONNECTIVITY: 如果使用证书启用 TLS,或者使用 `setup_peer_connection()` (API) 或 `--p2p` (CLI),则需要此项。请注意,网络连接不需要它。例如,像这样使用 frida-server 时不需要它:`frida-server -l 0.0.0.0`。 +- FRIDA_DATABASE: 如果使用 [SqliteDatabase](/docs/javascript-api/#sqlitedatabase) 和相关 API,则需要此项,否则可以安全地禁用。 +- FRIDA_JAVA_BRIDGE: 当想要在具有 Java 虚拟机或 Android Runtime 环境的进程内调用或插桩 Java API 时需要。请注意,除了 Java 之外,还有其他语言在 JVM 或 Android Runtime 上运行,例如 Kotlin 和 Scala。 +- FRIDA_OBJC_BRIDGE 和 FRIDA_SWIFT_BRIDGE: 当想要调用或插桩 Objective-C 或 Swift 代码时需要。在 Apple 操作系统(如 i/macOS)上有用,在 Apple 生态系统之外可以安全地禁用。 + +让我们浏览一下这些,看看不同的选项如何影响 Linux/armhf (32-bit ARM) 上的占用空间大小。 + +为了使以下内容更清晰一点,我们在 frida-core Meson 选项中添加了 `-Dassets=installed`。这意味着 frida-agent.so 不会嵌入到 frida-server/frida-inject 二进制文件中,而是从文件系统加载。 + +这也是您在嵌入式系统上通常想要的,因为将代理写出到 /tmp 有点浪费,无论它是由 flash 还是 tmpfs 支持的。 + +## 在 linux-armhf 上启用所有 config.mk 功能 + +{% highlight bash %} +3.8M frida-inject +3.2M frida-server + 15M frida-agent.so + 15M frida-gadget.so +{% endhighlight %} + +## 步骤 1: 禁用 V8 + +{% highlight bash %} +3.8M frida-inject +3.2M frida-server +5.2M frida-agent.so +5.3M frida-gadget.so +{% endhighlight %} + +Agent 减少了 9.8M。 + +## 步骤 2: 禁用连接功能(TLS 和 ICE),消除 OpenSSL + +{% highlight bash %} +2.6M frida-inject +2.0M frida-server +3.6M frida-agent.so +3.7M frida-gadget.so +{% endhighlight %} + +Agent 减少了 1.6M。 + +## 步骤 3: 禁用 GumJS 数据库 API,消除 SQLite + +{% highlight bash %} +2.6M frida-inject +2.0M frida-server +3.2M frida-agent.so +3.3M frida-gadget.so +{% endhighlight %} + +Agent 减少了 0.4M。 + +## 步骤 4: 禁用 GumJS bridges: ObjC, Swift, Java + +{% highlight bash %} +2.6M frida-inject +2.0M frida-server +2.8M frida-agent.so +2.9M frida-gadget.so +{% endhighlight %} + +Agent 减少了 0.4M。 + +让我们看看我们剩下了什么: + +![frida-agent.so footprint](/img/frida-agent-footprint.png "frida-agent.so footprint") + +为了满足我们的好奇心,让我们仔细看看三个突出的组件: + +![libcapstone.a footprint](/img/capstone-breakdown.png "libcapstone.a footprint") + +![libglib-2.0.a footprint](/img/glib-breakdown.png "libglib-2.0.a footprint") + +![libgio-2.0.a footprint](/img/gio-breakdown.png "libgio-2.0.a footprint") diff --git a/_i18n/cn/_docs/frida-cli.md b/_i18n/cn/_docs/frida-cli.md new file mode 100644 index 00000000..ddb810e1 --- /dev/null +++ b/_i18n/cn/_docs/frida-cli.md @@ -0,0 +1,79 @@ +Frida CLI 是一个 REPL 接口,旨在模拟 IPython(或 Cycript)的许多不错的功能,它试图让您更接近您的代码,以便进行快速原型设计和轻松调试。 + +{% highlight bash %} +# 通过 USB 将 Frida 连接到 iPad 并开始调试 Safari +$ frida -U Safari + +[USB::iPad 4::Safari]-> +{% endhighlight %} + +## 会话示例 + +{% highlight bash %} +# 将 Frida 连接到本地运行的 Calculator.app +$ frida Calculator + +# 查看局部变量/上下文 +[Local::ProcName::Calculator]-> +Backtracer Process +CpuContext Proxy +Dalvik Socket +DebugSymbol Stalker +File Thread +Frida WeakRef +Instruction clearInterval +Interceptor clearTimeout +Memory console +MemoryAccessMonitor gc +Module ptr +NULL recv +NativeCallback send +NativeFunction setInterval +NativePointer setTimeout +ObjC +# 查看通过 ObjC 接口暴露的内容 +[Local::ProcName::Calculator]-> ObjC. +Object implement selector +available mainQueue selectorAsString +classes schedule +# 列出前 10 个类(有很多!) +[Local::...::Calculator]-> Object.keys(ObjC.classes).slice(0, 10) +[ + "NSDrawer", + "GEOPDETAFilter", + "NSDeserializer", + "CBMutableCharacteristic", + "NSOrthographyCheckingResult", + "DDVariable", + "GEOVoltaireLocationShiftProvider", + "LSDocumentProxy", + "NSPreferencesModule", + "CIQRCodeGenerator" +] +{% endhighlight %} + +## 加载脚本 + +{% highlight bash %} +# 将 Frida 连接到本地运行的 Calculator.app 并加载 calc.js +$ frida Calculator -l calc.js + +# calc.js 中的代码现在已被加载并执行。任何更改都将自动重新加载 +[Local::ProcName::Calculator]-> + +# 随时从文件手动重新加载它 +[Local::ProcName::Calculator]-> %reload +[Local::ProcName::Calculator]-> +{% endhighlight %} + +## 启用 Node.js 兼容的调试器 + +{% highlight bash %} +# 将 Frida 连接到本地运行的 Calculator.app +# 并在启用调试器的情况下加载 calc.js +$ frida Calculator -l calc.js --debug + +Debugger listening on port 5858 +# 我们现在可以运行 node-inspector 并开始调试 calc.js +[Local::ProcName::Calculator]-> +{% endhighlight %} diff --git a/_i18n/cn/_docs/frida-discover.md b/_i18n/cn/_docs/frida-discover.md new file mode 100644 index 00000000..924e4b7a --- /dev/null +++ b/_i18n/cn/_docs/frida-discover.md @@ -0,0 +1 @@ +frida-discover 是一个用于发现程序内部函数的工具,然后可以使用 frida-trace 对其进行跟踪。 diff --git a/_i18n/cn/_docs/frida-kill.md b/_i18n/cn/_docs/frida-kill.md new file mode 100644 index 00000000..dd55f0b6 --- /dev/null +++ b/_i18n/cn/_docs/frida-kill.md @@ -0,0 +1,38 @@ +这是一个用于杀死进程的命令行工具。 + +您可以从 [frida-ps](/docs/frida-ps/) 工具获取 PID。 +{% highlight bash %} +$ frida-kill -D +# 列出活动应用程序 +$ frida-ps -D 1d07b5f6a7a72552aca8ab0e6b706f3f3958f63e -a + +PID Name Identifier +---- ------------------ ----------------------------------------------------- +4433 Camera com.apple.camera +4001 Cydia com.saurik.Cydia +4997 Filza com.tigisoftware.Filza +4130 IPA Installer com.slugrail.ipainstaller +3992 Mail com.apple.mobilemail +4888 Maps com.apple.Maps +6494 Messages com.apple.MobileSMS +5029 Safari com.apple.mobilesafari +4121 Settings com.apple.Preferences + +# 将 Frida 连接到设备并杀死正在运行的进程 +$ frida-kill -D 1d07b5f6a7a72552aca8ab0e6b706f3f3958f63e 5029 + +# 检查进程是否已被杀死 +$ frida-ps -D 1d07b5f6a7a72552aca8ab0e6b706f3f3958f63e -a + +PID Name Identifier +---- ------------------ ----------------------------------------------------- +4433 Camera com.apple.camera +4001 Cydia com.saurik.Cydia +4997 Filza com.tigisoftware.Filza +4130 IPA Installer com.slugrail.ipainstaller +3992 Mail com.apple.mobilemail +4888 Maps com.apple.Maps +6494 Messages com.apple.MobileSMS +4121 Settings com.apple.Preferences + +{% endhighlight %} diff --git a/_i18n/cn/_docs/frida-ls-devices.md b/_i18n/cn/_docs/frida-ls-devices.md new file mode 100644 index 00000000..30700267 --- /dev/null +++ b/_i18n/cn/_docs/frida-ls-devices.md @@ -0,0 +1,17 @@ +这是一个用于列出已连接设备的命令行工具,在与多个设备交互时非常有用。 + +{% highlight bash %} +# 通过 USB 将 Frida 连接到 iPad 并列出正在运行的进程 +$ frida-ls-devices + +# 输出示例 + +Id Type Name +---------------------------------------- ------ ---------------- +local local Local System +0216027d1d6d3a03 tether Samsung SM-G920F +1d07b5f6a7a72552aca8ab0e6b706f3f3958f63e tether iOS Device +tcp remote Local TCP + + +{% endhighlight %} diff --git a/_i18n/cn/_docs/frida-ps.md b/_i18n/cn/_docs/frida-ps.md new file mode 100644 index 00000000..dd485806 --- /dev/null +++ b/_i18n/cn/_docs/frida-ps.md @@ -0,0 +1,17 @@ +这是一个用于列出进程的命令行工具,在与远程系统交互时非常有用。 + +您可以从 [frida-ls-devices](/docs/frida-ls-devices) 工具获取设备 ID。 +{% highlight bash %} +# 通过 USB 将 Frida 连接到 iPad 并列出正在运行的进程 +$ frida-ps -U + +# 列出正在运行的应用程序 +$ frida-ps -Ua + +# 列出已安装的应用程序 +$ frida-ps -Uai + +# 将 Frida 连接到特定设备 +$ frida-ps -D 0216027d1d6d3a03 + +{% endhighlight %} diff --git a/_i18n/cn/_docs/frida-trace.md b/_i18n/cn/_docs/frida-trace.md new file mode 100644 index 00000000..1c175582 --- /dev/null +++ b/_i18n/cn/_docs/frida-trace.md @@ -0,0 +1,312 @@ +frida-trace 是一个用于动态跟踪函数调用的工具。 + +{% highlight bash %} +# 跟踪 Safari 中的 recv* 和 send* API,在日志中插入库名称 +$ frida-trace --decorate -i "recv*" -i "send*" Safari + +# 跟踪 Safari 中的 ObjC 方法调用 +$ frida-trace -m "-[NSView drawRect:]" Safari + +# 在 iPhone 上启动 SnapChat 并跟踪加密 API 调用 +$ frida-trace \ + -U \ + -f com.toyopagroup.picaboo \ + -I "libcommonCrypto*" + +# 在 Android 设备上启动 YouTube 并跟踪签名中带有 “certificate” 的 Java 方法 (s),忽略大小写 (i) +# 并且仅在用户定义的类中搜索 (u) +$ frida-trace \ + -U \ + -f com.google.android.youtube \ + --runtime=v8 \ + -j '*!*certificate*/isu' + +# 跟踪 Android 上 Samsung FaceService 应用中的所有 JNI 函数 +$ frida-trace -U -i "Java_*" com.samsung.faceservice + +# 跟踪 Windows 进程对 msvcrt.dll 中 "mem*" 函数的调用 +$ frida-trace -p 1372 -i "msvcrt.dll!*mem*" + +# 跟踪进程中匹配 "*open*" 的所有函数,msvcrt.dll 中的除外 +$ frida-trace -p 1372 -i "*open*" -x "msvcrt.dll!*open*" + +# 跟踪 libjpeg.so 中的未导出函数 +$ frida-trace -p 1372 -a "libjpeg.so!0x4793c" +{% endhighlight %} + +## 选项完整列表 + +{% highlight bash %} +$ frida-trace -h +usage: frida-trace [options] target + +positional arguments: + args 额外参数和/或目标 + +options: + -h, --help 显示此帮助信息并退出 + -D ID, --device ID 连接到具有给定 ID 的设备 + -U, --usb 连接到 USB 设备 + -R, --remote 连接到远程 frida-server + -H HOST, --host HOST 连接到 HOST 上的远程 frida-server + --certificate CERTIFICATE + 与 HOST 进行 TLS 通信,期望 CERTIFICATE + --origin ORIGIN 连接到远程服务器,设置 “Origin” 标头为 ORIGIN + --token TOKEN 使用 TOKEN 向 HOST 进行身份验证 + --keepalive-interval INTERVAL + 设置保活间隔(秒),或 0 以禁用(默认为 -1 以根据传输自动选择) + --p2p 与目标建立点对点连接 + --stun-server ADDRESS + 设置用于 --p2p 的 STUN 服务器 ADDRESS + --relay address,username,password,turn-{udp,tcp,tls} + 添加用于 --p2p 的中继 + -f TARGET, --file TARGET + 启动 FILE + -F, --attach-frontmost + 附加到最前端的应用程序 + -n NAME, --attach-name NAME + 附加到 NAME + -N IDENTIFIER, --attach-identifier IDENTIFIER + 附加到 IDENTIFIER + -p PID, --attach-pid PID + 附加到 PID + -W PATTERN, --await PATTERN + 等待匹配 PATTERN 的启动 + --stdio {inherit,pipe} + 启动时的 stdio 行为(默认为 “inherit”) + --aux option 启动时设置辅助选项,例如 “uid=(int)42”(支持的类型有:string, bool, int) + --realm {native,emulated} + 附加的领域 + --runtime {qjs,v8} 要使用的脚本运行时 + --debug 启用 Node.js 兼容的脚本调试器 + --squelch-crash 如果启用,将不会向控制台转储崩溃报告 + -O FILE, --options-file FILE + 包含额外命令行选项的文本文件 + --version 显示程序的版本号并退出 + -I MODULE, --include-module MODULE + 包含 MODULE + -X MODULE, --exclude-module MODULE + 排除 MODULE + -i FUNCTION, --include FUNCTION + 包含 [MODULE!]FUNCTION + -x FUNCTION, --exclude FUNCTION + 排除 [MODULE!]FUNCTION + -a MODULE!OFFSET, --add MODULE!OFFSET + 添加 MODULE!OFFSET + -T INCLUDE_IMPORTS, --include-imports INCLUDE_IMPORTS + 包含程序的导入 + -t MODULE, --include-module-imports MODULE + 包含 MODULE 导入 + -m OBJC_METHOD, --include-objc-method OBJC_METHOD + 包含 OBJC_METHOD + -M OBJC_METHOD, --exclude-objc-method OBJC_METHOD + 排除 OBJC_METHOD + -y SWIFT_FUNC, --include-swift-func SWIFT_FUNC + 包含 SWIFT_FUNC + -Y SWIFT_FUNC, --exclude-swift-func SWIFT_FUNC + 排除 SWIFT_FUNC + -j JAVA_METHOD, --include-java-method JAVA_METHOD + 包含 JAVA_METHOD + -J JAVA_METHOD, --exclude-java-method JAVA_METHOD + 排除 JAVA_METHOD + -s DEBUG_SYMBOL, --include-debug-symbol DEBUG_SYMBOL + 包含 DEBUG_SYMBOL + -q, --quiet 不格式化输出消息 + -d, --decorate 将模块名称添加到生成的 onEnter 日志语句 + -S PATH, --init-session PATH + 用于初始化会话的 JavaScript 文件的路径 + -P PARAMETERS_JSON, --parameters PARAMETERS_JSON + JSON 格式的参数,作为名为 'parameters' 的全局变量公开 + -o OUTPUT, --output OUTPUT + 将消息转储到文件 + --ui-port UI_PORT 提供 UI 的 TCP 端口 + +{% endhighlight %} + +## -U, --usb: 连接到 USB 设备 + +此选项告诉 `frida-trace` 在通过主机 USB 连接连接的远程设备上执行跟踪。 + +示例:您想从主机 Windows 机器跟踪 Android 设备上运行的应用程序。如果您指定 `-U / --usb`,frida-trace 将执行必要的工作,以便与远程设备传输所有数据并相应地进行跟踪。 + +
+
将 frida-server 二进制文件复制到远程设备
+

在跟踪远程设备时,请记住将 适合平台的 frida-server 二进制文件 复制到远程设备。复制后,请务必在开始跟踪会话之前运行 frida-server 二进制文件。

+

例如,要跟踪远程 Android 应用程序,您可以将 'frida-server-12.8.0-android-arm' 二进制文件复制到 Android 的 /data/local/tmp 文件夹。使用 adb shell,您可以在后台运行服务器(例如 "frida-server-12.8.0-android-arm &")。

+
+ +## -O: 通过文本文件传递命令行选项 + +使用此选项,您可以通过一个或多个文本文件传递任意数量的命令行选项。文本文件中的选项可以在一行或多行上,每行任意数量的选项,包括其他 `-O` 命令选项。 + +此功能对于处理大量命令行选项非常有用,并解决了命令行超过操作系统最大命令行长度的问题。 + +例如: + +{% highlight console %} +$ frida-trace -p 9753 --decorate -O additional-options.txt +{% endhighlight %} + +其中 additional-options.txt 是: + +{% highlight console %} +-i "gdi32full.dll!ExtTextOutW" +-S core.js -S ms-windows.js +-O module-offset-options.txt +{% endhighlight %} + +而 module-offset-options.txt 是: + +{% highlight console %} +-a "gdi32full.dll!0x3918DC" -a "gdi32full.dll!0xBE7458" +-a "gdi32full.dll!0xBF9904" +{% endhighlight %} + +## -I, -X: 包含/排除模块 + +这些选项允许您在一个选项中包含或排除特定模块(例如 *.so, *.dll)中的 **所有** 函数。该选项期望一个文件名 glob 来匹配一个或多个模块。任何匹配 glob 模式的模块都将被完整地包含或排除。 + +`frida-trace` 将为 `-I` 选项匹配的每个函数生成一个 JavaScript 处理程序文件。 + +要在包含整个模块后排除特定函数,请参阅 `-x` 选项。 + +## -i, -x: 包含/排除函数(基于 glob) + +这些选项使您能够根据需要包含或排除匹配的函数。这些是灵活的选项,允许从 **所有** 模块中的 **所有** 函数到特定模块中的单个函数的粒度范围。 + +`frida-trace` 将为 `-i` 选项匹配的每个函数生成一个 JavaScript 处理程序文件。 + +`-i / -x` 选项在语法上与其大写对应项不同,因为它们接受以下任何形式(MODULE 和 FUNCTION 都是 glob 模式): + +
+- MODULE!FUNCTION
+- FUNCTION
+- !FUNCTION
+- MODULE!
+
+ +以下是一些示例及其说明: + +| 选项值 | 说明 | +| --------------------- | ---------------------------------------------------------------- | +| -i "msvcrt.dll!*cpy*" | 匹配名称中带有 'cpy' 的所有函数,仅在 msvcrt.dll 中 | +| -i "*free*" | 匹配所有模块中名称中带有 'free' 的所有函数 | +| -i "!*free*" | 与 -i "*free*" 相同 | +| -i "gdi32.dll!" | 跟踪 gdi32.dll 中的所有函数(与 -I "gdi32.dll" 相同) | + +
+
frida-trace 的工作集以及包含和排除的顺序
+

frida-trace 有一个“工作集”的内部概念,即一组“模块:函数”对,其处理程序将在运行时被跟踪。工作集的内容可以通过包含/排除命令行选项 (-I / -X / -i / -x) 进行更改。

+

重要的是要理解包含/排除选项的顺序很重要。每个此类选项都在工作集的当前状态上工作,不同的选项顺序可能导致不同的结果。换句话说,包含/排除选项是过程性的(即顺序很重要),而不仅仅是声明性的。

+

例如,假设我们要跟踪正在运行的进程中所有模块中的所有 "str*" 和 "mem*" 函数。在我们的示例中,这些函数位于三个模块中:ucrtbase.dll, ntdll.dll, 和 msvcrt.dll。然而,为了减少噪音,我们不想跟踪在 msvcrt.dll 模块中找到的任何函数。

+

我们将描述命令行上的三种不同选项顺序,并表明它们产生不同的结果。

+
    +
  • -i "str*" -i "mem*" -X "msvcrt.dll" +
  • +
      +
    • '-i "str*"'
      匹配 3 个模块中的 80 个函数,工作集有 80 个条目
    • +
    • '-i "mem*"'
      匹配 3 个模块中的 18 个函数,工作集有 98 个条目
    • +
    • '-X "msvcrt.dll"'
      移除源自 msvcrt.dll 的 28 个 "str" 和 6 个 "mem" 函数,最终工作集有 64 个条目
    • +
    +
  • -i "str*" -X "msvcrt.dll" -i "mem*" +
  • +
      +
    • '-i "str*"'
      匹配 3 个模块中的 80 个函数,工作集有 80 个条目
    • +
    • '-X "msvcrt.dll"'
      移除源自 msvcrt.dll 的 28 个 "str" 函数,工作集有 52 个条目。
    • +
    • '-i "mem*"'
      匹配 3 个模块(包括 msvcrt.dll)中的 18 个函数,最终工作集有 70 个条目
    • +
    +
  • -X "msvcrt.dll" -i "str*" -i "mem*" +
  • +
      +
    • '-X "msvcrt.dll"'
      尝试移除源自 msvcrt.dll 的 28 个 "str" 和 6 个 "mem" 函数。由于工作集为空,因此没有要移除的内容,工作集有 0 个条目。
    • +
    • '-i "str*"'
      匹配 3 个模块中的 80 个函数,工作集有 80 个条目
    • +
    • '-i "mem*"'
      匹配 3 个模块中的 18 个函数,最终工作集有 98 个条目
    • +
    +
+
+ +## -a: 包含函数(基于偏移量) + +此选项允许跟踪其名称未由其父模块导出的函数(例如,静态 C/C++ 函数)。只要您知道该函数入口点的绝对偏移量,这就不应阻止您跟踪此类函数。 + +示例:`-a "libjpeg.so!0x4793c"` + +在此示例中,选项的值提供了模块的全名(即 `libjpeg.so`)和模块内函数入口点的十六进制偏移量(`0x4793c`)。 + +`frida-trace` 将为 `-a` 选项匹配的每个函数生成一个 JavaScript 处理程序文件。 + +## -P: 使用全局可访问的 JSON 对象初始化 frida-trace 会话 + +此选项允许将 JSON 对象分配给 `parameters` 全局变量。您的处理程序可以访问此全局变量,使您能够通过修改命令行上传递的 `-P` 的值来动态更改处理程序的行为。 + +传递的 JSON 对象可以随您所愿地复杂或广泛,只要它是有效的 JSON。 + +
+
示例
+

+ 在您的会话中,您正在跟踪许多函数。有时您希望所有处理程序打印出它们的进程 ID。使用 `-P` 选项,您可以启用处理程序来决定是否打印进程 ID。 +

+

+ 首先,确定通知处理程序是否应显示进程 ID 的 JSON 对象格式。让我们使用以下格式: + +
+
+

+ -P '{"displayPid": true}' +
+
+ + 请注意,此形式是您在 Linux 下可能使用的形式(即,您可以在命令行上同时使用单引号和双引号)。在 Windows 下,您只能使用双引号,因此您应该通过插入 两个 双引号来转义内部双引号,如下所示: + +
+
+
+ -P "{""displayPid"": true}" +
+
+ + Frida-trace 将把您的 JSON 对象分配给全局 JavaScript 变量 "parameters"。现在,您的处理程序可以检查 parameters.displayPid 变量以决定是否打印进程 ID: + +
+
+ + { + onEnter(log, args, state) { + log('memcpy() [msvcrt.dll]'); + if (parameters.displayPid) { + log(`Process ID: ${Process.id}`); + } + }, + + onLeave(log, retval, state) { + } +} + + +
+

+
+ +## -S: 使用 JavaScript 代码初始化 frida-trace 会话 + +此选项通过执行您选择的一个或多个 JavaScript 代码文件来初始化您的 frida-trace 会话,这些文件可以声明全局可见的函数并将任意数据添加到全局 "state" 对象。当 "state" 对象传递给您的任何处理程序时,您可以立即访问您在会话初始化期间保存到其中的任何内容。 + +此强大功能的用途包括在会话开始之前初始化 frida-trace 运行环境,以及共享可以在不同处理程序和开发项目中调用的微调和调试过的 JavaScript 函数和数据。 + +有关如何使用此强大功能的详细说明,请参阅[会话初始化入门]({% link _docs/frida-trace/session-initialization-primer.md %})。 + +## -d, --decorate: 将模块名称添加到日志跟踪 + +`--decorate` 选项在 `frida-trace` 自动生成 JavaScript 处理程序脚本时相关。默认情况下,处理程序的 `onEnter` 函数如下所示: + +onEnter(log, args, state) { + log('memcpy()'); +}, + + +缺点是,如果同一函数名存在于多个模块中,则很难区分函数跟踪。`--decorate` 函数指示 `frida-trace` 在默认的 `onEnter` 跟踪指令中插入模块名称: + +onEnter(log, args, state) { + log('memcpy() [msvcrt.dll]'); +}, + diff --git a/_i18n/cn/_docs/frida-trace/session-initialization-primer.md b/_i18n/cn/_docs/frida-trace/session-initialization-primer.md new file mode 100644 index 00000000..b1f73460 --- /dev/null +++ b/_i18n/cn/_docs/frida-trace/session-initialization-primer.md @@ -0,0 +1,366 @@ +此页面描述了 frida-trace `--init-session` / `-S` 命令行选项的用途,以及如何在您的工作中使用它。 + +## 什么是 --init-session 选项? + +`--init-session` 选项在 frida-trace 引擎初始化阶段执行任意数量的用户编写的 JavaScript 代码文件。这些文件在调用第一个函数处理程序之前执行。 + +它的强大之处在于能够定义全局可见的函数并将数据存储在全局 `state` 对象中,该对象作为参数传递给调用的每个处理程序。 + +`state` 对象允许您在函数调用之间维护信息。存储在 `state` 中的数据可供所有调用的处理程序访问。 + +## --init-session 选项的用途 + +`--init-session` / `-S` 选项保证您选择的 JavaScript 源代码在 frida-trace 引擎开始跟踪之前执行。此功能的可能应用包括: + +1. 执行自定义代码,创建您选择的代码和数据对象,并在调用第一个函数处理程序之前执行此操作。 +2. 创建共享代码库,允许共享微调和调试过的 JavaScript 代码,这些代码可以由任何处理程序在任何时间全局调用。 + +frida-trace JavaScript 代码通常编写为一次性的“用完即弃”代码。但是,如果您发现自己经常在处理程序和项目之间复制和粘贴代码,请考虑将代码保存在共享库中。编写并调试后,您可以在未来的项目中重用这些函数和数据。 + +## 详细示例:创建共享代码库 + +在此示例中,我们演示使用 `--init-session` / `-S` 选项来增强对 Microsoft Windows `ExtTextOutW()` 函数的跟踪。我们以自上而下的方式描述组件,从 ExtTextOutW.js JavaScript 处理程序函数开始,一直到共享代码文件。 + +### ExtTextOutW():函数签名 + +在我的 Windows 系统中,要监视的 [ExtTextOutW()][] 函数位于 *gdi32full.dll* 中。这是该函数的 C 语法: + +{% highlight c %} +BOOL ExtTextOutA( + HDC hdc, + int x, + int y, + UINT options, + const RECT *lprect, + LPCSTR lpString, + UINT c, + const INT *lpDx +); +{% endhighlight %} + +利用我们共享代码库中的 JavaScript 代码,我们生成增强的跟踪输出。 + +### 增强的跟踪输出 + +在展示处理程序代码和共享代码库之前,这里是增强的跟踪输出本身: + +{% highlight console %} +c:\project> frida-trace -p 6980 --decorate -i "gdi32full.dll!ExtTextOutW" -S core.js -S ms-windows.js +Instrumenting... +ExtTextOutW: Loaded handler at "c:\\project\\__handlers__\\gdi32full.dll\\ExtTextOutW.js" +Started tracing 1 function. Press Ctrl+C to stop. + /* TID 0x3ab8 */ + 2695 ms --------------------------------------------- + 2695 ms ExtTextOutW() [gdi32full.dll] + 2695 ms x: 0 + 2695 ms y: 0 + 2695 ms options: ETO_OPAQUE + 2695 ms lprect [20 bytes] + 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF + 00b9c81c 00 00 00 00 01 00 00 00 01 00 00 00 98 02 00 00 ................ + 00b9c82c 26 90 b2 b3 &... + 2695 ms lprect: (left, top, right, bottom) = (0, 1, 1, 664) + 2695 ms c: 0 + 2696 ms x (exit): 0 + 2696 ms y (exit): 0 + . . . + 2788 ms --------------------------------------------- + 2788 ms ExtTextOutW() [gdi32full.dll] + 2788 ms x: 1 + 2788 ms y: 0 + 2788 ms options: ETO_CLIPPED | ETO_IGNORELANGUAGE + 2788 ms lprect [20 bytes] + 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF + 00b9eaac 00 00 00 00 00 00 00 00 4d 00 00 00 0f 00 00 00 ........M....... + 00b9eabc 0a 78 7e c1 .x~. + 2788 ms lprect: (left, top, right, bottom) = (0, 0, 77, 15) + 2788 ms lpString [50 bytes] + 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF + 1aa90148 43 00 61 00 6c 00 69 00 62 00 72 00 69 00 20 00 C.a.l.i.b.r.i. . + 1aa90158 28 00 42 00 6f 00 64 00 79 00 29 00 29 00 75 00 (.B.o.d.y.).).u. + 1aa90168 20 00 77 00 61 00 6e 00 20 00 20 00 74 00 6f 00 .w.a.n. . .t.o. + 1aa90178 20 00 . + 2788 ms *lpString: "Calibri (Body))u wan to do" + 2788 ms c: 14 + 2788 ms lpDx [4 bytes] + 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF + 09e71208 08 00 00 00 .... + 2788 ms *lpDx: 8 + 2789 ms x (exit): 0 + 2789 ms y (exit): 0 +{% endhighlight %} + +注意以下跟踪增强: + +- `options` 字段转换为文本形式 +- `lprect` 内存指针显示为十六进制内存转储和文本字符串 +- `lpString` 内存指针显示为十六进制内存转储和文本字符串 +- `lpDx` 整数指针显示为十六进制内存转储和整数 + +文本转换和十六进制内存转储是我们共享代码库中的函数。让我们看看我们的 `ExtTextOutW()` 处理程序 JavaScript 代码,它将使用共享代码。 + +### 处理程序:ExtTextOutW.js + +这是我们的 ExeTextOutW() 处理程序代码。我们通过调用共享代码函数来增强它。这些函数存在于外部作用域中,因为在会话脚本中定义的任何顶级函数对所有处理程序都可见。这些函数是在 frida-trace 通过 `--init-session` / `-S` 命令行选项执行共享代码 JavaScript 文件时定义的。 + +{% highlight js %} +/* + * Auto-generated by Frida. Please modify to match the signature of ExtTextOutW. + * This stub is currently auto-generated from manpages when available. + * + * For full API reference, see: https://frida.re/docs/javascript-api/ + */ + +{ + onEnter(log, args, state) { + /* + * C syntax: + * + * BOOL ExtTextOutW( + * HDC hdc, + * int x, + * int y, + * UINT options, + * const RECT *lprect, + * LPCWSTR lpString, + * UINT c, + * const INT *lpDx + * ); + */ + + log('---------------------------------------------'); + log('ExtTextOutW() [gdi32full.dll]'); + + cloneArgs(args, 8, this); + const [, x, y, options, lprect, lpString, c, lpDx] = this.args; + + log(`x: ${x.toInt32()}`); + log(`y: ${y.toInt32()}`); + + log(`options: ${decodeExttextoutOptions(options)}`); + + if (!lprect.isNull()) { + prettyHexdump(log, 'lprect', lprect, 20); + log(`lprect: ${rectStructToString(lprect)}`); + } + + if (!lpString.isNull()) { + prettyHexdump(log, 'lpString', lpString, 50); + log(`*lpString: "${lpString.readUtf16String()}"`); + } + + log(`c: ${c.toUInt32()}`); + + if (!lpDx.isNull()) { + prettyHexdump(log, 'lpDx', lpDx, 4); + log(`*lpDx: ${lpDx.readU32()}`); + } + }, + + onLeave(log, retval, state) { + /* + * We can access the onEnter arguments using `this.args`, + * as cloneArgs() copied them there. + */ + const [, x, y] = this.args; + log(`x (exit): ${x.toInt32()}`); + log(`y (exit): ${y.toInt32()}`); + } +} +{% endhighlight %} + +请注意,除了调用标准 Frida 函数(例如 `toInt32()`、`isNull()`、`readUtf16String()`)之外,还有对我们共享代码函数的调用(例如 `cloneArgs()`、`decodeExttextoutOptions()`、`prettyHexdump()`、`rectStructToString()`)。共享代码函数已经过调试和完善,随时可以被任何处理程序调用。 + +### 共享代码:core.js + +“core.js”共享代码库包含核心或基本函数,旨在供 frida-trace 处理程序和共享代码库重用。 + +编写共享代码库很简单:您的共享库源文件定义函数和数据对象,将后者存储在全局 `state` 对象中。一旦存储在那里,任何处理程序都可以通过 `state.propertyName` 访问它们。 + +{% highlight js %} +/* + * Collection of useful general-purpose Frida handler functions. + */ + +/** + * Creates a true JavaScript array as `invCtx.args`. This array can be accessed + * in the handler's onLeave(). + * + * @param {NativePointer[]} args - The `args` array as passed to onEnter(). + * @param {number} numArgs - The number of meaningful arguments in `args`. This + * function has no way of determining the number of actual arguments because + * `args` is a virtual array. + * @param {InvocationContext} invCtx - The `this` object of the calling onEnter(). + * @returns {NativePointer[]} Copy of `args`. + */ +function cloneArgs(args, numArgs, invCtx) { + const items = []; + for (let i = 0; i !== numArgs; i++) + items.push(args[i]); + invCtx.args = items; +} + +/** + * Returns a string describing the bitflags set in `value`. + * + * @param {number} value - A value consisting of zero or more bitflags. + * @param {Map} spec - A Map between: + * [hex value] -> [flag descriptive string] + * For example: + * new Map([ + * [0x0004: 'ETO_CLIPPED'], + * [0x0010: 'ETO_GLYPH_INDEX'], + * ... + * ]) + * @returns {string} Flag names delimited by '|', or '0' if none are set. + */ +function decodeBitflags(value, spec) { + if (value === 0) + return '0'; + + const flags = []; + let pending = value; + + for (const [flagValue, flagName] of spec.entries()) { + if ((value & flagValue) !== 0) { + flags.push(flagName); + pending &= ~flagValue; + if (pending === 0) + break; + } + } + + if (pending !== 0) + flags.push(`0x${pending.toString(16)}`); + + return flags.join(' | '); +} + +/** + * Outputs the hex dump results to the log stream. If you only want the dump + * lines without outputing them to the log stream, use prettyHexdumpLines(). + * + * @param {function} log - The log function to output to. + * @param {string} desc - Descriptive text, printed together with the hex dump. + * @param {NativePointer} address - Memory location to dump. + * @param {number} length - Number of bytes to dump. + */ +function prettyHexdump(log, desc, address, length) { + const lines = []; + prettyHexdumpLines(lines, desc, address, length); + log(lines.join('\n')); +} + +/** + * Produces a somewhat more elegant hex dump, based on Frida's own hexdump(). + * It does not output the hex dump to any stream, but rather returns the hex + * dump lines in an array. It is up to the caller to decide where to, and how, + * to output the dump lines. + * + * @param {string[]} lines - Caller-provided array that will return the hex dump + * lines. + * @param {string} desc - Descriptive text, printed together with the hex dump. + * @param {NativePointer} address - Memory location to dump. + * @param {number} length - Number of bytes to dump. + * @param {string} [indent='\t\t'] - String to prepend to each dump line. + */ +function prettyHexdumpLines(lines, desc, address, length, indent = '\t\t') { + lines.push(`${desc} [${length} bytes]`); + + try { + const s = hexdump(address, { length }); + for (const line of s.split('\n')) { + lines.push(`${indent}${line}`); + } + } catch (e) { + lines.push(`${indent}WARNING: address is NOT VALID (${address})`); + } +} +{% endhighlight %} + +### 共享代码:ms-windows.js + +“ms-windows.js”共享代码库包含与 MS Windows 相关的实用函数,并构建在 `core.js` 库之上。 + +{% highlight js %} +/* + * Collection of useful Frida handler functions for MS Windows. + */ + +const extTextOptionsSpec = new Map([ + [0x00004, 'ETO_CLIPPED'], + [0x00010, 'ETO_GLYPH_INDEX'], + [0x01000, 'ETO_IGNORELANGUAGE'], + [0x00800, 'ETO_NUMERICSLATIN'], + [0x00400, 'ETO_NUMERICSLOCAL'], + [0x00002, 'ETO_OPAQUE'], + [0x02000, 'ETO_PDY'], + [0x00080, 'ETO_RTLREADING'], + [0x10000, 'ETO_REVERSE_INDEX_MAP'], +]); + +/** + * Decodes ExtTextOutW() `options` bit flags. + * + * @param {number} flags - A DWORD consisting of the `options` bit flags used + * by ExtTextOutW(). + * @returns {string} Options delimited by '|', or '0' if none are set. + */ +function decodeExttextoutOptions(flags) { + return decodeBitflags(flags, extTextOptionsSpec); +} + +/** + * Returns the four RECT values as a string of the form: + * (left, top, right, bottom) = (0, 0, 77, 15) + * + * @param {NativePointer} lprect - Pointer to a Windows RECT object. The memory + * consists of four (4) contiguous LONG values, corresponding to the left, + * top, right, and bottom values, respectively. + * @returns {string} Description of the RECT. + */ +function rectStructToString(lprect) { + if (lprect.isNull()) { + return 'LPRECT is null'; + } + + const left = lprect.readU32(); + const top = lprect.add(4).readU32(); + const right = lprect.add(8).readU32(); + const bottom = lprect.add(12).readU32(); + + return `(left, top, right, bottom) = (${left}, ${top}, ${right}, ${bottom})`; +} +{% endhighlight %} + +两个 `-S` 命令行选项提供了“core.js”和“ms-windows.js”共享库源文件的路径。 + +用光标触摸 Word 应用程序中的任何内容都会生成 ExtTextOutW() 跟踪。 + +## 考虑要点 + +以下是使用 `-S` 选项时需要考虑的一些要点。 + +### 使用不同的代码源文件对共享函数进行分组 + +为了清晰起见,您可以为不同的函数组拥有多个共享代码文件。在上面的示例中,通用和基本函数位于“core.js”中,而 MS Windows 特定函数位于“ms-windows.js”中。在其他项目中,您可能有用于 Android 相关函数、Linux 函数等的文件。 + +### 实现命名空间 + +随着您使用的共享库代码文件数量的增加,您可能会遇到由于“命名空间污染”而导致的名称冲突。如果两个不同的共享代码文件实现具有相同名称的函数,则可能会发生这种情况。如果在您自己的组织内,您可以修改名称。但是,如果您使用的是第三方共享代码库,这可能会更困难。 + +一种可能的解决方案是让共享库将其函数存储在描述性命名的全局对象上,并相应地将数据存储在“state”上。 + +以下是您可能如何实现它: + +{% highlight js %} +global.MyLibrary = { + doX() { + }, + doY() { + } +}; +{% endhighlight %} + + +[ExtTextOutW()]: https://web.archive.org/web/20191222090821/https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-exttextoutw diff --git a/_i18n/cn/_docs/functions.md b/_i18n/cn/_docs/functions.md new file mode 100644 index 00000000..f9644433 --- /dev/null +++ b/_i18n/cn/_docs/functions.md @@ -0,0 +1,391 @@ +我们展示了如何使用 Frida 在函数被调用时检查它们,修改它们的参数,以及对目标进程内的函数进行自定义调用。 + +## 设置实验 + +创建一个文件 `hello.c`: + +{% highlight c %} +#include +#include + +void +f (int n) +{ + printf ("Number: %d\n", n); +} + +int +main (int argc, + char * argv[]) +{ + int i = 0; + + printf ("f() is at %p\n", f); + + while (1) + { + f (i++); + sleep (1); + } +} +{% endhighlight %} + +使用以下命令编译: + +{% highlight bash %} +$ gcc -Wall hello.c -o hello +{% endhighlight %} + +启动程序并记下 `f()` 的地址(在以下示例中为 `0x400544`): + +{% highlight bash %} +f() is at 0x400544 +Number: 0 +Number: 1 +Number: 2 +… +{% endhighlight %} + +## Hook 函数 + +以下脚本展示了如何 hook 目标进程内的函数调用并将函数参数报告给您。创建一个包含以下内容的文件 `hook.py`: + +{% highlight py %} +import frida +import sys + +session = frida.attach("hello") +script = session.create_script(""" +Interceptor.attach(ptr("%s"), { + onEnter(args) { + send(args[0].toInt32()); + } +}); +""" % int(sys.argv[1], 16)) +def on_message(message, data): + print(message) +script.on('message', on_message) +script.load() +sys.stdin.read() +{% endhighlight %} + +使用您从上面挑选出的地址运行此脚本(在我们的示例中为 `0x400544`): + +{% highlight bash %} +$ python hook.py 0x400544 +{% endhighlight %} + +这应该每秒给您一条如下形式的新消息: + +{% highlight py %} +{'type': 'send', 'payload': 531} +{'type': 'send', 'payload': 532} +… +{% endhighlight %} + +## 修改函数参数 + +接下来:我们想要修改传递给目标进程内函数的参数。创建具有以下内容的 `modify.py` 文件: + +{% highlight py %} +import frida +import sys + +session = frida.attach("hello") +script = session.create_script(""" +Interceptor.attach(ptr("%s"), { + onEnter(args) { + args[0] = ptr("1337"); + } +}); +""" % int(sys.argv[1], 16)) +script.load() +sys.stdin.read() +{% endhighlight %} + +针对 `hello` 进程运行此脚本(它应该仍在运行): + +{% highlight bash %} +$ python modify.py 0x400544 +{% endhighlight %} + +此时,运行 `hello` 进程的终端应该停止计数并始终报告 `1337`,直到您按 `Ctrl-D` 从中分离。 + +{% highlight bash %} +Number: 1281 +Number: 1282 +Number: 1337 +Number: 1337 +Number: 1337 +Number: 1337 +Number: 1287 +Number: 1288 +Number: 1289 +… +{% endhighlight %} + +## 调用函数 + +我们可以使用 Frida 调用目标进程内的函数。创建具有以下内容的 `call.py` 文件: + +{% highlight py %} +import frida +import sys + +session = frida.attach("hello") +script = session.create_script(""" +const f = new NativeFunction(ptr("%s"), 'void', ['int']); +f(1911); +f(1911); +f(1911); +""" % int(sys.argv[1], 16)) +script.load() +{% endhighlight %} + +运行脚本: + +{% highlight bash %} +$ python call.py 0x400544 +{% endhighlight %} + +并密切关注(仍在)运行 `hello` 的终端: + +{% highlight bash %} +Number: 1879 +Number: 1911 +Number: 1911 +Number: 1911 +Number: 1880 +… +{% endhighlight %} + +## 实验 2 - 注入字符串并调用函数 + +注入整数非常有用,但我们也可以注入字符串,实际上,还可以注入您进行模糊测试/测试所需的任何其他类型的对象。 + +创建一个新文件 `hi.c`: + +{% highlight c %} +#include +#include + +int +f (const char * s) +{ + printf ("String: %s\n", s); + return 0; +} + +int +main (int argc, + char * argv[]) +{ + const char * s = "Testing!"; + + printf ("f() is at %p\n", f); + printf ("s is at %p\n", s); + + while (1) + { + f (s); + sleep (1); + } +} +{% endhighlight %} + +与之前类似,我们可以创建一个脚本 `stringhook.py`,使用 Frida 将字符串注入内存,然后以以下方式调用函数 f(): + +{% highlight py %} +import frida +import sys + +session = frida.attach("hi") +script = session.create_script(""" +const st = Memory.allocUtf8String("TESTMEPLZ!"); +const f = new NativeFunction(ptr("%s"), 'int', ['pointer']); + // In NativeFunction param 2 is the return value type, + // and param 3 is an array of input types +f(st); +""" % int(sys.argv[1], 16)) +def on_message(message, data): + print(message) +script.on('message', on_message) +script.load() +{% endhighlight %} + +密切关注 `hi` 的输出,您应该看到类似以下的内容: + +{% highlight bash %} +... +String: Testing! +String: Testing! +String: TESTMEPLZ! +String: Testing! +String: Testing! +... +{% endhighlight %} + +使用类似的方法,如 `Memory.alloc()` 和 `Memory.protect()` 来轻松操作进程内存。将其与 python `ctypes` 库结合使用,可以创建其他内存对象(如 `structs`),将其作为字节数组加载,然后作为指针参数传递给函数。 + +## 注入恶意内存对象 - 示例:sockaddr_in 结构体 + +任何做过网络编程的人都知道,C 语言中最常用的数据类型之一是 `struct`。这是一个简单的程序示例,它创建一个网络套接字,并通过端口 5000 连接到服务器,并通过连接发送字符串 `"Hello there!"` 来宣布自己。 + +{% highlight c %} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int +main (int argc, + char * argv[]) +{ + int sock_fd, i, n; + struct sockaddr_in serv_addr; + unsigned char * b; + const char * message; + char recv_buf[1024]; + + if (argc != 2) + { + fprintf (stderr, "Usage: %s \n", argv[0]); + return 1; + } + + printf ("connect() is at: %p\n", connect); + + if ((sock_fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) + { + perror ("Unable to create socket"); + return 1; + } + + bzero (&serv_addr, sizeof (serv_addr)); + + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons (5000); + + if (inet_pton (AF_INET, argv[1], &serv_addr.sin_addr) <= 0) + { + fprintf (stderr, "Unable to parse IP address\n"); + return 1; + } + printf ("\nHere's the serv_addr buffer:\n"); + b = (unsigned char *) &serv_addr; + for (i = 0; i != sizeof (serv_addr); i++) + printf ("%s%02x", (i != 0) ? " " : "", b[i]); + + printf ("\n\nPress ENTER key to Continue\n"); + while (getchar () == EOF && ferror (stdin) && errno == EINTR) + ; + + if (connect (sock_fd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) + { + perror ("Unable to connect"); + return 1; + } + + message = "Hello there!"; + if (send (sock_fd, message, strlen (message), 0) < 0) + { + perror ("Unable to send"); + return 1; + } + + while (1) + { + n = recv (sock_fd, recv_buf, sizeof (recv_buf) - 1, 0); + if (n == -1 && errno == EINTR) + continue; + else if (n <= 0) + break; + recv_buf[n] = 0; + + fputs (recv_buf, stdout); + } + + if (n < 0) + { + perror ("Unable to read"); + } + + return 0; +} +{% endhighlight %} + +这是相当标准的代码,并调用作为第一个参数给出的任何 IP 地址。如果您运行 `nc -lp 5000` 并在另一个终端窗口中运行 `./client 127.0.0.1`,您应该看到消息出现在 netcat 中,并且还能够发回消息给 `client`。 + +现在,我们可以开始找点乐子了 - 正如我们在上面看到的,我们可以将字符串和指针注入到进程中。我们可以通过操作程序作为其操作的一部分吐出的结构体 `sockaddr_in` 来做同样的事情: + +{% highlight bash %} +$ ./client 127.0.0.1 +connect() is at: 0x400780 + +Here's the serv_addr buffer: +02 00 13 88 7f 00 00 01 30 30 30 30 30 30 30 30 +Press ENTER key to Continue +{% endhighlight %} + +如果您不完全熟悉 struct 的结构,网上有很多资源会告诉您这是什么。这里重要的部分是字节 `0x1388`,即十进制的 5000。这是我们的端口号(后面的 4 个字节是十六进制的 IP 地址)。如果我们将其更改为 `0x1389`,那么我们可以将客户端重定向到不同的端口。如果我们更改接下来的 4 个字节,我们可以完全更改客户端指向的 IP 地址! + +这是一个将恶意结构体注入内存,然后劫持 `libc.so` 中的 `connect()` 函数以将我们的新结构体作为其参数的脚本。 + +创建如下文件 `struct_mod.py`: + +{% highlight py %} +import frida +import sys + +session = frida.attach("client") +script = session.create_script(""" +// First, let's give ourselves a bit of memory to put our struct in: +send('Allocating memory and writing bytes...'); +const st = Memory.alloc(16); +// Now we need to fill it - this is a bit blunt, but works... +st.writeByteArray([0x02, 0x00, 0x13, 0x89, 0x7F, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30]); +// Module.getGlobalExportByName() can find functions without knowing the source +// module, but it's slower, especially over large binaries! YMMV... +Interceptor.attach(Module.getGlobalExportByName('connect'), { + onEnter(args) { + send('Injecting malicious byte array:'); + args[1] = st; + } + //, onLeave(retval) { + // retval.replace(0); // Use this to manipulate the return value + //} +}); +""") + +# Here's some message handling.. +# [ It's a little bit more meaningful to read as output :-D +# Errors get [!] and messages get [i] prefixes. ] +def on_message(message, data): + if message['type'] == 'error': + print("[!] " + message['stack']) + elif message['type'] == 'send': + print("[i] " + message['payload']) + else: + print(message) +script.on('message', on_message) +script.load() +sys.stdin.read() +{% endhighlight %} + +请注意,此脚本演示了如何使用 `Module.getGlobalExportByName()` API 在我们的目标中按名称查找任何导出的函数。如果我们可以提供模块,那么在较大的二进制文件上会更快,但这在这里不太重要。 + +现在,运行 `./client 127.0.0.1`,在另一个终端运行 `nc -lp 5001`,在第三个终端运行 `./struct_mod.py`。一旦我们的脚本运行,在 `client` 终端窗口中按 ENTER,netcat 现在应该显示客户端发送的字符串。 + +我们已经成功劫持了原始网络,方法是将我们自己的数据对象注入内存并使用 Frida hook 我们的进程,并使用 `Interceptor` 来完成我们在操作函数方面的肮脏工作。 + +这展示了 Frida 的真正力量 - 无需修补、复杂的逆向工程,也无需花费数小时盯着反汇编代码。 + +这是一个演示上述内容的快速视频: + +https://www.youtube.com/watch?v=cTcM7R872Ls diff --git a/_i18n/cn/_docs/gadget.md b/_i18n/cn/_docs/gadget.md new file mode 100644 index 00000000..7c251ab3 --- /dev/null +++ b/_i18n/cn/_docs/gadget.md @@ -0,0 +1,263 @@ +Frida 的 Gadget 是一个共享库,旨在当[注入][]操作模式不适用时,由要插桩的程序加载。 + +这可以通过多种方式完成,例如: + +- 修改程序的源代码 +- 修补它或其库之一,例如使用像 [insert_dylib][] 这样的工具 +- 使用像 *LD_PRELOAD* 或 *DYLD_INSERT_LIBRARIES* 这样的动态链接器功能 + +一旦动态链接器执行其构造函数,Gadget 就会启动。 + +它支持四种不同的交互,具体取决于您的用例,其中 [Listen](#listen) 交互是默认的。您可以通过添加配置文件来覆盖此设置。该文件的命名应与 Gadget 二进制文件完全相同,但文件扩展名为 *.config*。例如,如果您将二进制文件命名为 *FridaGadget.dylib*,则应将配置文件命名为 *FridaGadget.config*。 + +请注意,您可以随意命名 Gadget 二进制文件,这对于躲避反 Frida 检测方案(查找名称中带有 "Frida" 的加载库)非常有用。 + +还值得注意的是,当使用 Xcode 将 .config 添加到 iOS 应用时,您可能会发现它倾向于将 *FridaGadget.dylib* 放在名为 “Frameworks” 的子目录中,而将 “.config” 放在其上级目录中 – 与应用的可执行文件和任何资源文件相邻。因此,在这种情况下,Gadget 也会在父目录中查找 .config。但前提是它被放在名为 “Frameworks” 的目录中。 + +在 Android 上,包管理器只会从不可调试应用程序的 `/lib` 目录复制文件,如果它们的名称符合以下条件: +- 以前缀 `lib` 开头 +- 以后缀 `.so` 结尾 +- 是 `gdbserver` + +Frida 非常清楚这个限制,并将接受带有这些更改的配置文件。示例: +``` +lib +└── arm64-v8a + ├── libgadget.config.so + ├── libgadget.so +``` +有关更多信息,请查看[这篇文章](https://lief.quarkslab.com/doc/latest/tutorials/09_frida_lief.html#id9)。 + +配置文件应该是以 JSON 对象为根的 UTF-8 编码文本文件。它在根级别支持四个不同的键: + +- `interaction`: 描述要使用的交互的对象。默认为 [Listen](#listen) 交互。 + +- `teardown`: 指定 `minimal` 或 `full` 的字符串,说明库卸载时要执行多少清理工作。默认为 `minimal`,这意味着我们不会关闭内部线程并释放分配的内存和操作系统资源。如果 Gadget 的生命周期与程序本身相关联,这很好。如果您打算在某个时候卸载它,请指定 `full`。 + +- `runtime`: 指定 `default`、`qjs` 或 `v8` 的字符串,让您覆盖使用的默认 JavaScript 运行时。 + +- `code_signing`: 指定 `optional` 或 `required` 的字符串,通过将其设置为 `required`,可以在没有附加调试器的情况下在未越狱的 iOS 设备上运行。默认为 `optional`,这意味着 Frida 将假定可以修改内存中的现有代码并运行未签名的代码,而不会被内核杀死。将其设置为 `required` 也意味着 Interceptor API 不可用。因此,在未越狱的 iOS 设备上使用 Interceptor API 的唯一方法是在加载 Gadget 之前附加调试器。请注意,只需使用调试器启动应用程序即可,它不必保持附加状态,因为宽松的代码签名状态一旦设置就会保持。 + +## 支持的交互类型 + + 1. [Listen](#listen) + 1. [Connect](#connect) + 1. [Script](#script) + 1. [ScriptDirectory](#scriptdirectory) + +## Listen + +这是默认交互,其中 Gadget 暴露一个兼容 *frida-server* 的接口,默认监听 *localhost:27042*。唯一的区别是正在运行的进程和已安装的应用列表仅包含一个条目,即程序本身。进程名称始终只是 *Gadget*,已安装应用的标识符始终是 *re.frida.Gadget*。 + +为了实现早期插桩,我们让 Gadget 的构造函数阻塞,直到您 *attach()* 到进程,或者在经过通常的 *spawn()* -> *attach()* -> *…应用插桩…* 步骤后调用 *resume()*。这意味着现有的 CLI 工具(如 [frida-trace](/docs/frida-trace/))的工作方式与您已经使用它们的方式相同。 + +如果您不想要这种阻塞行为,并希望让程序直接启动,或者您更喜欢它在不同的接口或端口上监听,您可以通过配置文件自定义它。 + +默认配置为: + +{% highlight json %} +{ + "interaction": { + "type": "listen", + "address": "127.0.0.1", + "port": 27042, + "on_port_conflict": "fail", + "on_load": "wait" + } +} +{% endhighlight %} + +支持的配置键有: + +- `address`: 指定要监听的接口的字符串。支持 IPv4 和 IPv6。默认为 `127.0.0.1`。指定 `0.0.0.0` 以监听所有 IPv4 接口,`::` 以监听所有 IPv6 接口。 + +- `port`: 指定要监听的 TCP 端口的数字。默认为 `27042`。 + +- `certificate`: 指定此项以启用 TLS。必须是 PEM 编码的公钥和私钥,可以是包含多行 PEM 数据的字符串,也可以是指定要从中加载的文件系统路径的单行字符串。服务器将接受来自客户端的任何证书。 + +- `token`: 指定此项以启用身份验证。必须是指定来自传入客户端的预期秘密令牌的字符串。 + +- `on_port_conflict`: 指定 `fail` 或 `pick-next` 的字符串,说明如果监听端口已被占用该怎么办。默认为 `fail`,这意味着 Gadget 将启动失败。如果您希望它尝试每个连续端口直到找到可用端口,请指定 `pick-next`。 + +- `on_load`: 指定 `resume` 或 `wait` 的字符串,说明加载 Gadget 时要做什么。默认为 `wait`,这意味着它将等待您连接到它并告诉它恢复。如果您希望允许程序立即启动,请指定 `resume`,如果您只想稍后附加,这很有用。 + +- `origin`: 指定此项以通过要求 “Origin” 标头与此处指定的值匹配来防止来自 Web 浏览器的未经授权的跨域使用。 + +- `asset_root`: 指定此项以通过 HTTP/HTTPS 提供静态文件,其中暴露指定目录内的任何可访问文件。默认情况下不提供文件。 + +## Connect + +这是 “Listen” 交互的反面,Gadget 不是在 TCP 上监听,而是连接到正在运行的 *frida-portal* 并成为其进程集群中的一个节点。这就是它监听的所谓 *cluster* 接口。Portal 通常还暴露一个 *control* 接口,该接口使用与 *frida-server* 相同的协议。这允许任何连接的控制器 *enumerate_processes()* 并 *attach()* 到它们,就好像它们在运行 Portal 的机器本地一样。 + +为了实现早期插桩,我们让 Gadget 的构造函数阻塞,直到控制器请求 *resume()* – 但前提是启用了 spawn-gating。(通过 *Device.enable_spawn_gating()*。)这意味着对于简单的设置,Gadget 只会阻塞直到它连接到 Portal 并加入其集群 – 以便询问是否启用了 spawn-gating。 + +默认配置为: + +{% highlight json %} +{ + "interaction": { + "type": "connect", + "address": "127.0.0.1", + "port": 27052 + } +} +{% endhighlight %} + +支持的配置键有: + +- `address`: 指定要连接的主机的字符串,即 Portal 的集群接口暴露的地方。支持 IPv4 和 IPv6。默认为 `127.0.0.1`。 + +- `port`: 指定要连接的 TCP 端口的数字,在暴露 Portal 集群接口的主机上。默认为 `27052`。 + +- `certificate`: 如果 Portal 启用了 TLS,则必须指定。包含 PEM 编码的公钥,可以是包含多行 PEM 数据的字符串,也可以是指定要从中加载的文件系统路径的单行字符串。这是受信任 CA 的公钥,服务器的证书必须匹配或派生自该公钥。 + +- `token`: 如果 Portal 的集群接口启用了身份验证,则必须指定。这是指定要呈现给 Portal 的令牌的字符串。此字符串的实际解释取决于 Portal 实现,从 *frida-portal* 情况下的固定秘密,到 API 实例化 Portal 并插入自定义身份验证服务时的任何内容(例如 OAuth 访问令牌)。 + +- `acl`: 指定访问控制列表的字符串数组,用于限制哪些控制器能够发现并与此进程交互。例如,如果是 `["team-a", "team-b"]`,则来自 “team-a” 或 “team-b” 的任何控制器都将被授予访问权限。仅当通过 API 实例化 Portal 时才应设置此键,因为需要自定义应用程序代码来 *标记* 要授予访问权限的控制器连接,通常基于某种自定义身份验证方案。 + +
+
高级用户
+

+ 为了获得更大的控制权,例如自定义身份验证、每节点 ACL 和特定于应用程序的协议消息,您也可以实例化 PortalService 对象,而不是运行 frida-portal CLI 程序。 +

+
+ +## Script + +有时,通过在程序入口点执行之前从文件系统加载脚本,以完全自主的方式应用一些插桩是很有用的。 + +这是所需的最小配置: + +{% highlight json %} +{ + "interaction": { + "type": "script", + "path": "/home/oleavr/explore.js" + } +} +{% endhighlight %} + +其中 *explore.js* 包含以下骨架: + +{% highlight js %} +rpc.exports = { + init(stage, parameters) { + console.log('[init]', stage, JSON.stringify(parameters)); + + Interceptor.attach(Module.getGlobalExportByName('open'), { + onEnter(args) { + const path = args[0].readUtf8String(); + console.log('open("' + path + '")'); + } + }); + }, + dispose() { + console.log('[dispose]'); + } +}; +{% endhighlight %} + +[rpc.exports][] 部分实际上是可选的,当您的脚本需要通过其生命周期感知时很有用。 + +Gadget 调用您的 `init()` 方法并等待其返回,然后再让程序执行其入口点。这意味着如果您需要执行某些异步操作(例如 [Socket.connect()][]),您可以返回一个 *Promise*,并保证您不会错过任何早期调用。 +第一个参数 `stage` 是一个字符串,指定 `early` 或 `late`,用于了解 Gadget 是刚刚加载,还是脚本正在重新加载。下面有关于后一个主题的更多信息。 +第二个参数 `parameters` 是配置文件中可选指定的对象,如果没有则为空对象。这对参数化您的脚本很有用。 + +如果您需要在卸载脚本时执行一些显式清理,您还可以暴露一个 `dispose()` 方法。这通常发生在进程退出、Gadget 被卸载或在从磁盘加载新版本之前卸载脚本时。 + +为了调试,您可以使用 *console.log()*、*console.warn()* 和 *console.error()*,它们将打印到 *stdout*/*stderr*。 + +支持的配置键有: + +- `path`: 指定要加载的脚本的文件系统路径的字符串。也可以是相对于 Gadget 二进制文件所在位置的路径。在 iOS 上指定相对路径将首先查找相对于应用 Documents 目录的脚本。这意味着您可以使用 iTunes 文件共享上传脚本的更新版本,或者通过 AFC 提供整个容器来更新它,这对于可调试的应用程序是允许的。这与 `"on_change": "reload"` 一起使用特别有用。 + 此键没有默认值,必须提供。 + +- `parameters`: 包含您希望传递给 `init()` RPC 方法的任意配置数据的对象。默认为空对象。 + +- `on_change`: 指定 `ignore` 或 `reload` 的字符串,其中 `ignore` 表示脚本将仅加载一次,`reload` 表示 Gadget 将监视文件并在其更改时重新加载脚本。默认为 `ignore`,但强烈建议在开发期间使用 `reload`。 + +## ScriptDirectory + +在某些情况下,您可能希望篡改系统范围的程序和库,但与其从脚本逻辑中识别程序,不如进行一些最小的过滤,并根据 Gadget 运行所在的程序加载不同的脚本。您甚至可能不需要任何过滤,但发现将每个脚本视为单独的插件很方便。在 GNU/Linux 系统上,此类脚本甚至可以由包提供,从而可以轻松安装对现有应用程序的调整。 + +这是所需的最小配置: + +{% highlight json %} +{ + "interaction": { + "type": "script-directory", + "path": "/usr/local/frida/scripts" + } +} +{% endhighlight %} + +支持的配置键有: + +- `path`: 指定包含要加载的脚本的目录的文件系统路径的字符串。也可以是相对于 Gadget 二进制文件所在位置的路径。此键没有默认值,必须提供。 + 脚本应使用 *.js* 作为其文件扩展名,每个脚本还可以在其旁边的 *.config* 文件中包含配置数据。这意味着 + *twitter.js* 可以在名为 *twitter.config* 的文件中指定其配置。 + +- `on_change`: 指定 `ignore` 或 `rescan` 的字符串,其中 `ignore` 表示目录将仅扫描一次,`rescan` 表示 Gadget 将监视目录并在其更改时重新扫描。默认为 `ignore`,但强烈建议在开发期间使用 `rescan`。 + +每个脚本的可选配置文件可能包含以下键: + +- `filter`: 包含此脚本加载条件的对象。只需匹配其中一个,因此如果需要,应在脚本本身中实现复杂的过滤。支持以下键指定要匹配的内容: + + - `executables`: 指定可执行文件名称的字符串数组 + - `bundles`: 指定 bundle 标识符的字符串数组 + - `objc_classes`: 指定 Objective-C 类名的字符串数组 + +- `parameters`: 包含您希望传递给 `init()` RPC 方法的任意配置数据的对象。默认为空对象。 + +- `on_change`: 指定 `ignore` 或 `reload` 的字符串,其中 `ignore` 表示脚本将仅加载一次,`reload` 表示 Gadget 将监视文件并在其更改时重新加载脚本。默认为 `ignore`,但强烈建议在开发期间使用 `reload`。 + +假设您想为 Twitter 的 macOS 应用编写一个调整,您可以在 */usr/local/frida/scripts* 中创建一个名为 *twitter.js* 的文件,其中包含: + +{% highlight js %} +const { TMTheme } = ObjC.classes; + +rpc.exports = { + init(stage, parameters) { + console.log('[init]', stage, JSON.stringify(parameters)); + + ObjC.schedule(ObjC.mainQueue, () => { + TMTheme.switchToTheme_(TMTheme.darkTheme()); + }); + }, + dispose() { + console.log('[dispose]'); + + ObjC.schedule(ObjC.mainQueue, () => { + TMTheme.switchToTheme_(TMTheme.lightTheme()); + }); + } +}; +{% endhighlight %} + +然后,为了确保此脚本仅加载到该特定应用中,您将创建另一个名为 *twitter.config* 的文件,其中包含: + +{% highlight json %} +{ + "filter": { + "executables": ["Twitter"], + "bundles": ["com.twitter.twitter-mac"], + "objc_classes": ["Twitter"] + } +} +{% endhighlight %} + +此示例表示如果满足以下任一条件,我们希望加载脚本: + +- 可执行文件名称是 `Twitter`,或者 +- 其 bundle 标识符是 `com.twitter.twitter-mac`,或者 +- 它加载了一个名为 `Twitter` 的 Objective-C 类。 + +对于这个特定的例子,您可能只会过滤 bundle ID,因为那是通过最稳定的标识符,如果需要,在代码中进行兼容性检查。 + +除了 `filter` 键之外,您还可以指定 `parameters` 和 `on_change`,就像上面的 [Script](#script) 配置一样。 + + +[Injected]: /docs/modes/#injected +[insert_dylib]: https://github.com/Tyilo/insert_dylib +[rpc.exports]: /docs/javascript-api/#rpc +[Socket.connect()]: /docs/javascript-api/#socket diff --git a/_i18n/cn/_docs/go-api.md b/_i18n/cn/_docs/go-api.md new file mode 100644 index 00000000..1cac7805 --- /dev/null +++ b/_i18n/cn/_docs/go-api.md @@ -0,0 +1,49 @@ +Go 绑定使您可以轻松地从 [Go][] 使用 Frida 的 API。 + +提供的一些功能包括: + +* 列出设备/应用程序/进程 +* 附加到应用程序/进程 +* 获取有关设备/应用程序/进程的信息 + +有关完整文档,请访问 [pkg.go.dev][]。 + +## 示例 + +{% highlight golang %} +package main + +import ( + "fmt" + + "github.com/frida/frida-go/frida" +) + +func main() { + manager := frida.NewDeviceManager() + devices, err := manager.EnumerateDevices() + if err != nil { + panic(err) + } + + fmt.Printf("[*] Frida version: %s\n", frida.Version()) + fmt.Println("[*] Devices: ") + for _, device := range devices { + fmt.Printf("[*] %s => %s\n", device.Name(), device.ID()) + } +} +{% endhighlight %} + +上面的示例应该输出类似以下内容: + +{% highlight bash %} +$ go build main.go && ./main +[*] Frida version: 16.0.3 +[*] Devices: +[*] Local System => local +[*] Local Socket => socket +{% endhighlight %} + + +[Go]: https://go.dev/ +[pkg.go.dev]: https://pkg.go.dev/github.com/frida/frida-go/frida diff --git a/_i18n/cn/_docs/gsoc-ideas-2015.md b/_i18n/cn/_docs/gsoc-ideas-2015.md new file mode 100644 index 00000000..b4631766 --- /dev/null +++ b/_i18n/cn/_docs/gsoc-ideas-2015.md @@ -0,0 +1,117 @@ +## 让 Android 成为 Frida 的一等公民 + +### 2015 年更新:感谢 [NowSecure][],这现在已在 Frida 中实现。 + +**简要说明:** 虽然 Frida 目前确实支持 Android,但有两个缺失的部分导致在插桩 Android 应用程序时产生大量摩擦,我们需要改进这些方面: + +- 打包:打包 frida-server 并将其作为系统守护程序运行或捆绑启动器应用程序。 + +- 集成:添加一个 Android 后端,用于自动化 USB 设备发现和端口转发。这类似于 Frida 的 iOS «Fruity» 后端,它与 iTunes 的 usbmuxd 集成,有效地自动化设备发现和 TCP 端口转发,因此用户只需插入设备并在几秒钟内开始插桩移动应用程序。 +在实现方面,这应该是关于与 adb 集成,或者嵌入 adb 的核心,这将: + - 枚举连接的 Android 设备并在发生热插拔事件时通知应用程序。 + - 根据需要自动转发端口;不再需要在用户每次附加到新进程时都进行容易出错的 `adb forward`。 + +**预期结果:** Android 包。设备发现和自动端口转发。 + +**知识先决条件:** Vala, C + +**可能的导师:** Ole André Vadla Ravnås <[oleavr@gmail.com](mailto:oleavr@gmail.com)>, Karl Trygve Kalleberg <[karltk@gmail.com](mailto:karltk@gmail.com)> + + +## 添加对在 ART VM 中运行的 Android 应用程序的支持 + +**简要说明:** Frida 目前支持 Dalvik,虽然大部分代码只是与 VM 实现的 JNI API 交互,但有一些部分是特定于 VM 的。当前代码可以在 [此处](https://github.com/frida/frida-gum/blob/42b69917976f43ba3ec4297046b319970dc037dd/gum/gumscript-runtime-dalvik.js) 找到。 +添加对 ART VM 的支持应该只是改进该实现以添加特定于 ART 的部分,然后公开一个统一的 API。当前的 `Dalvik` 模块将只是一个已弃用的别名,保留到下一个主要的 Frida 版本。 + +**预期结果:** ART VM 支持。 + +**知识先决条件:** JavaScript, C + +**可能的导师:** Ole André Vadla Ravnås <[oleavr@gmail.com](mailto:oleavr@gmail.com)>, Karl Trygve Kalleberg <[karltk@gmail.com](mailto:karltk@gmail.com)> + + +## 将 Stalker 移植到 ARM + +**简要说明:** Frida 的 Stalker 是一个基于动态重新编译的非常强大的代码跟踪引擎。它目前仅适用于 x86。将其移植到 ARM 将允许 [CryptoShark](https://github.com/frida/cryptoshark) 用于移动应用程序。 + +想了解更多关于它是如何为 x86 实现的吗?在 [此处](https://medium.com/@oleavr/anatomy-of-a-code-tracer-b081aadb0df8) 阅读更多内容。 + +**预期结果:** Stalker 能够在 ARM 上跟踪代码。 + +**知识先决条件:** C, Assembly + +**可能的导师:** Ole André Vadla Ravnås <[oleavr@gmail.com](mailto:oleavr@gmail.com)>, Karl Trygve Kalleberg <[karltk@gmail.com](mailto:karltk@gmail.com)> + + +## 将 Stalker 移植到 ARM64 + +**简要说明:** Frida 的 Stalker 是一个基于动态重新编译的非常强大的代码跟踪引擎。它目前仅适用于 x86。将其移植到 ARM64 将允许 [CryptoShark](https://github.com/frida/cryptoshark) 用于移动应用程序。 + +想了解更多关于它是如何为 x86 实现的吗?在 [此处](https://medium.com/@oleavr/anatomy-of-a-code-tracer-b081aadb0df8) 阅读更多内容。 + +**预期结果:** Stalker 能够在 ARM64 上跟踪代码。 + +**知识先决条件:** C, Assembly + +**可能的导师:** Ole André Vadla Ravnås <[oleavr@gmail.com](mailto:oleavr@gmail.com)>, Karl Trygve Kalleberg <[karltk@gmail.com](mailto:karltk@gmail.com)> + + +## 添加对在 Android 上 spawn 应用程序的支持:从第一条指令开始插桩 + +### 2015 年更新:感谢 [NowSecure][],这现在已在 Frida 中实现。 + +**简要说明:** 不要与 Frida 中已经存在的 spawn 进程的支持混淆,这是关于添加对从 Zygote fork 自身以运行应用程序后执行的第一条指令开始插桩 Android 应用程序的支持。 + +**预期结果:** 用于 spawn Android 应用程序的 API。 + +**知识先决条件:** Vala, C + +**可能的导师:** Ole André Vadla Ravnås <[oleavr@gmail.com](mailto:oleavr@gmail.com)>, Karl Trygve Kalleberg <[karltk@gmail.com](mailto:karltk@gmail.com)> + + +## 为主要发行版打包 + +**简要说明:** 我们应该让 Linux 用户更容易上手,并通过尽可能多地出现在生态系统中来提高 Frida 的知名度。 + +**预期结果:** Frida 的 buildbot 自动发布主要发行版的软件包。 + +**知识先决条件:** python + +**可能的导师:** Ole André Vadla Ravnås <[oleavr@gmail.com](mailto:oleavr@gmail.com)>, Karl Trygve Kalleberg <[karltk@gmail.com](mailto:karltk@gmail.com)> + + +## 将 Frida 移植到 Windows Phone + +**简要说明:** Frida 目前支持 Windows、macOS、Linux、iOS、Android 和 QNX,但遗憾的是尚不支持 Windows Phone。添加对 WP 的支持将需要: + +- 一个注入器,用于将 Frida 的共享库注入到目标进程中 + +- 进程 spawn 支持 + +- 与 CLR 运行时动态交互的 JavaScript 运行时,类似于 Frida 的 JS 环境中内置的 [Dalvik JS 运行时](https://github.com/frida/frida-gum/blob/42b69917976f43ba3ec4297046b319970dc037dd/gum/gumscript-runtime-dalvik.js)。 + +前两项可能类似于当前的 Windows 后端,尽管可能要简单得多。 + +**预期结果:** 支持插桩 Windows Phone 应用程序。 + +**知识先决条件:** JavaScript, C, CLR + +**可能的导师:** Ole André Vadla Ravnås <[oleavr@gmail.com](mailto:oleavr@gmail.com)>, Karl Trygve Kalleberg <[karltk@gmail.com](mailto:karltk@gmail.com)> + + +## 向 JavaScript 暴露回溯器和符号解析 API + +### 2015 年更新:感谢 [NowSecure][],这现在已在 Frida 中实现。 + +**简要说明:** frida-gum 中目前有 [Backtracer](https://github.com/frida/frida-gum/blob/42b69917976f43ba3ec4297046b319970dc037dd/gum/gumbacktracer.h) 和 [符号解析](https://github.com/frida/frida-gum/blob/42b69917976f43ba3ec4297046b319970dc037dd/gum/gumsymbolutil.h) API,尚未暴露给 JS 运行时。 + +然而,符号解析 API 不仅仅是暴露此 API 的问题,因为底层实现将需要一些调整才能在注入到另一个进程时正常工作。Windows 实现目前依赖于加载 DbgHelp.dll,这可能不是一个可接受的约束。 + +**预期结果:** JavaScript 运行时中可用的回溯器和符号解析 API。 + +**知识先决条件:** JavaScript, C + +**可能的导师:** Ole André Vadla Ravnås <[oleavr@gmail.com](mailto:oleavr@gmail.com)>, Karl Trygve Kalleberg <[karltk@gmail.com](mailto:karltk@gmail.com)> + + +[NowSecure]: https://www.nowsecure.com/ diff --git a/_i18n/cn/_docs/gsod-ideas-2023.md b/_i18n/cn/_docs/gsod-ideas-2023.md new file mode 100644 index 00000000..61c13af6 --- /dev/null +++ b/_i18n/cn/_docs/gsod-ideas-2023.md @@ -0,0 +1,135 @@ +# 规划更新 Frida 文档 + +_规划如何让 Frida 文档变得更好_ + +本文档检查了 Frida 文档的当前状态,并讨论了如何使其变得更好。 + +## 文档和支持的当前状态 + +### 文档的当前可用性 + +1. [官方 Frida 文档 (frida.re/docs)](https://frida.re/docs/) TODO: 描述文档中目前的内容。 +1. [Learn Frida and Frida Handbook](https://learnfrida.info/),作者 [Fernando Diaz](https://learnfrida.info/about_faq/)。 +该网站有在线文档 (HTML),并且可以从同一网站以及 NowSecure Academy 网站自由访问该书。 +1. _其他位置?_ + +### 用户支持 + +1. 用户可以在 [Telegram 上获得 Frida 支持](https://frida.re/contact/)。目前有 2720 名成员。_Frida OffTopic_ 有 457 名成员。 +1. 用户可以在 [IRC/Freenode #frida 上获得 Frida 支持](https://frida.re/contact/)。频道上只有不到十个人,可能由于 Freenode 和 Libera 之间的分裂而不活跃。 +1. Libera Chat 上有一个 #frida 频道。我访问时该频道有 13 个用户。frida.re 网站上尚未列出该频道的存在。 +1. [github/frida/frida 上的 Github Issues](https://github.com/frida/frida/issues)。Github Issues 被(滥)用作帮助场所。 +1. [Discord Frida Server](https://discord.gg/J7VCWhZQ5N)。180 名成员,我上次访问时有 40 人在线(欧盟时区)。 + +## 理由(用于更新 Frida 文档) + +每个免费/开源项目都应该让人们知道和理解它的作用。这增强了可行性,并可能为项目带来更多贡献者。 + +维护项目最困难的部分是软件开发方面。文档和社区的发展更容易,不应被忽视。 + +## 本文档的受众类型 + +文档应迎合以下用户群体 + +1. 具有其他计算机相关任务经验并希望扩展到 Frida 的高级用户。 + 在解释 Frida 时,文档应与他们先前的知识联系起来。 +1. 具有 Frida 经验并希望查阅文档以快速提醒某些任务的用户。 + 文档不应仅以截屏视频的形式存在,而应以文本形式存在,以便轻松复制粘贴复杂的命令。命令应该易于识别。当您选择一行时,它不应选择 Unix 提示符。 +1. 计算机经验很少但愿意努力学习的用户。 + 他们应该很好地理解 Frida 的作用,能够成功设置 Frida,并至少执行一项简单的任务。 +1. 对 Frida 的应用感兴趣但会要求其他人承担任务/工作的用户。 + 他们应该能够很好地理解 Frida 的作用,并能够大致评估任务/工作的难度。 + +## 文档的目的 + +* 避免支持渠道上的重复问题。 +* 展示常见任务的最佳实践。 +* 涵盖各种操作系统上的初始成功安装。包括故障排除。包括验证安装是否成功的简单验证任务。 +* 文档应该可以通过搜索引擎访问。大多数用户在搜索引擎上搜索。常见搜索应指向文档。它也会被那些 AI 搜索引擎获取。 + +## 新文档中应该包含什么 + +* 讨论文章:Frida 到底是什么?_访问正在运行的软件的地址空间_的类型。以 _Cheat Engine_ 为例。Cheat Engine 对数据段进行读/写/设置以更改生命数或硬币数。较新的 Cheat Engine [也进行代码注入,使用汇编!](https://wiki.cheatengine.org/index.php?title=Tutorials:Auto_Assembler:Injection_full)。 +* 讨论文章:Frida 到底是什么?使用 Greasemonkey/Tampermonkey/Violentmonkey 进行实际解释。实际上,[Violentmonkey](https://github.com/violentmonkey/violentmonkey) 是开发活跃的一个。 +* 安装,Windows/Linux/OSX 的一般说明,每个主要 OS 版本的单独文章,带有故障排除部分和验证其安装成功的验证示例。单独的文章存在是为了让搜索引擎可以获取它们并供新用户使用。 +* 参考文章:assets 中的那些不同包是什么,https://github.com/frida/frida/releases(即 _code devkit_, _gum devkit_,...) +* Android:如何在已 root 的手机上设置 Frida +* Android:如何将 Gadget 注入 APK,首先如何从手机获取 APK。[使用 apk.sh](https://github.com/ax/apk.sh)。 +* Code Share:解释如何使用 https://codeshare.frida.re/,如何贡献, +* 桌面:展示如何在三个主要桌面上使用 Frida。 +* _TODO_ + +## TODO + +* 在 https://github.com/frida/frida/issues 上创建新 Issue 时添加文档。指导如何为错误报告收集更好的信息。应该说明这不是支持场所。 +* 使用论坛软件?也许不是 discord(围墙花园,搜索引擎无法访问)。[像 StackExchange 一样管理](https://area51.stackexchange.com/) 或 [像 Discourse 一样自托管](https://github.com/discourse/discourse)。 + + +## Google 文档季的组织提案 + +[关于创建组织提案的一般说明](https://developers.google.com/season-of-docs/docs/organization-application-hints)。 + +我们遵循 https://developers.google.com/season-of-docs/docs/org-proposal-template 上的模板 + +提案现在开始: + +## 更新 Frida 的网站文档 + +### 关于您的组织 + +[Frida](https://frida.re)(当前版本 16.0.11,2013 年首次发布)是一个 wxWindows Library Licence 许可的动态代码插桩软件工具包。它附加到正在运行的软件,并让您访问执行流和数据。它允许您注入用 Javascript 编写的自己的代码,以便您可以修改软件的运行方式。Frida 通常用于计算机安全领域的逆向工程,例如 [Google Project Zero 的这个案例](https://googleprojectzero.blogspot.com/2022/01/zooming-in-on-zero-click-exploits.html)。Frida 是逆向工程 Android 和 iOS 移动应用程序的首选工具。此外,Frida 还用于软件测试、调试和软件开发。Frida 目前支持九种操作系统和三种架构系列(Intel, ARM, MIPS)。最后,Frida 是该领域最受欢迎的软件。 + +### 关于您的项目 + +[官方 Frida 文档](https://frida.re/docs/) 需要重组和扩展。它是由高级用户编写的,对新用户来说太简略了。新用户最终会在项目的 github issues 上提问(每周大约十个问题)。有一个拥有 2730 名用户的 Telegram 频道,但很难提供支持;给出的任何答案都很难被下一个问同样问题的人发现。 + +需要创建一个摩擦日志,帮助识别知识差距并提供故障排除文档。用例应包括在许多受支持的操作系统上设置 Frida,并提供验证设置是否有效的步骤。应提供讨论文档,向不同技术经验和背景水平的受众解释 Frida 的作用。 + +Frida 是安全研究人员使用的工具之一。有一个此类开源安全工具的生态系统,包括 AFL++(安全模糊测试),ghidra(反编译)。安全研究人员将使用其中一种或混合使用这些工具来完成任务。通过改进文档,Frida 将在支持生态系统和发展自己的社区方面处于更有利的地位。 + +### 您的项目范围 + +Frida 项目将: + +* 审计现有文档并为三个主要用例(为不同操作系统设置 Frida,使用 Frida gadget 设置 Frida,以及使用 Frida 的常见任务)创建摩擦日志。 +* 使用摩擦日志作为了解文档差距的指南,为主要用例创建更新的文档。 +* 创建一个快速“备忘单”,帮助新用户快速有效地安装和使用 Frida。 +* 整合来自文档测试人员(项目中的志愿者)和更广泛的 Frida 社区的反馈。 +* 与发布团队合作更新 Frida 网站上的文档,并创建一个流程以使文档与更新工具保持同步。 +* 为 Github Issues 创建问题模板,以便用户如果提出支持问题,将被重定向到官方文档和支持站点。添加用于报告错误和功能请求的模板。 +* 浏览 1300 个 Github issues 并适当标记那些是支持请求的问题。用作文档中的输入。 +* 包括 Github Releases 中不同类型资产的文档以及应如何使用它们。 +* 将 https://codeshare.frida.re/ 纳入 Frida 文档。 + +本项目范围之外的工作: + +* 本项目不会为 Frida 的代码贡献生成详细文档。 + +我们有一位该项目的强大技术写作候选人,我们估计这项工作将需要六个月才能完成。@simos 已承诺支持该项目。 + +### 衡量您的项目的成功 + +如果在发布新文档后出现以下情况,我们将认为该项目是成功的: + +* 90% 的新用户问题得到覆盖。 +* 实际上是支持请求的 Github issues 数量下降到每周两个。 + +### 时间表 + +该项目本身大约需要六个月才能完成。一旦聘请了技术作家,我们将花一个月的时间进行技术作家入职培训,然后进入审计和摩擦日志,并在最后几个月专注于创建文档。 + +|日期 |行动项目 | +|-----------------------|-----------------------------------------------------------| +|五月 |入职培训 | +|六月 - 七月 |审计现有文档并创建摩擦日志 | +|八月 - 十月 |创建文档 | +|十一月 |项目完成 | + +### 项目预算 + +|预算项目 |预算 (USD) |实际 (USD) | 备注 | +|---------------------------------|------------------------------|------------------|----------| +|技术作家 |$12,000 | $12,000 | | +|志愿者津贴 (3 x $500) |$1,500 | $13,500 | 用于将密切提供信息和/或审查交付成果的志愿者 | +|志愿者 T 恤 |$200 | $13,700 | 打印并交付给有文档贡献的志愿者 | +|总计 | | $13,700 | diff --git a/_i18n/cn/_docs/gum-graft.md b/_i18n/cn/_docs/gum-graft.md new file mode 100644 index 00000000..d40fbdfb --- /dev/null +++ b/_i18n/cn/_docs/gum-graft.md @@ -0,0 +1,21 @@ +`gum-graft` 工具用于提前修补二进制文件,以允许 Interceptor 在禁止运行时代码修改的环境中对它们进行插桩。目前,这仅指实施严格代码签名策略的 Apple 移动操作系统——即在没有附加调试器的情况下运行应用程序的未越狱系统。在这种情况下,覆盖 [Gadget][] `code_signing` 选项并将其设置为 `required`。 + +您可以从 [releases 页面][]下载 `gum-graft`。 + +{% highlight bash %} +Usage: + gum-graft [OPTION?] BINARY - graft instrumentation into Mach-O binaries + +Help Options: + -h, --help Show help options + +Application Options: + -i, --instrument=0x1234 Include instrumentation for a specific code offset + -s, --ingest-function-starts Include instrumentation for offsets retrieved from LC_FUNCTION_STARTS + -m, --ingest-imports Include instrumentation for imports + -z, --transform-lazy-binds Transform lazy binds into regular binds (experimental) +{% endhighlight %} + + +[Gadget]: https://frida.re/docs/gadget/ +[releases page]: https://github.com/frida/frida/releases diff --git a/_i18n/cn/_docs/hacking.md b/_i18n/cn/_docs/hacking.md new file mode 100644 index 00000000..c64eb701 --- /dev/null +++ b/_i18n/cn/_docs/hacking.md @@ -0,0 +1,50 @@ +## 架构 + +
+
7RvbkqO49Wu6MnkwBQZfeGz3dG8yNcl2Ve9sMk9TAoStGYwI4La9X7/nCAkDFr5CuzOJH2x00OXoXC+S7+yH5eaXlCSLf/CARndDM9jc2R/vhsOJ68A3ArYFwHGsAjBPWVCAKoAX9geVQFNCVyygWa1jznmUs6QO9HkcUz+vwUIeySXkZAmZq+l3gBefRPvQf7EgXxTQ6XC8g/+NsvlCLWON3eJNlm/VHAENySrKBwIE7/D1kqi5JCIbs2iqFbeyLadLSFxD6A/OlzVASrOSUnLKkEmsZNvjaUBTuYyERSz+USWb/QicSzmHkfi03DzQCLmnOFMMe2p5W1IrpXFt7bYB9sT0xtTz3NC1ych1BrYUjlcSreRu9ui5XrCcviTEx/YaZOzOni3yZQQtCx5DHudSaizY0IxEbI7E8wEn3P4sZFH0wCOOpIh5DB1nAckWFHHCCSQGNM2plFnNvnbUAjmnfEnzdItslEIuyStFXInueidCJWxRER/bkUAieTIvZ94RER4kHU+l6einoKkzviFRdSQcR7DCjOByKQ0FdPyfFaoOkC5Ha3SP8w+f5ixfrDzDB5UdPoVg1GBM8TuIwTruhqEpI+mcFpgr6DcvIvGPaq/xHH9rMwhcYFqYunirYAF7bYKQmzX2q5nxxaCwI4C6OU02+6t+Iq/kxU9ZglMAkCxRVorvshl7mWyLBTM0X7oFwfzQgWIWLmkZSNjGksAdeC5ng10WE9Z3hTQVG9sD10hwnuB3ILeu9BVKbKf7cmsp0a7K7cg+W2y/ZDT91fuOXg9MO/HA8XYoqz5Pr5RVOcMpsvrhLJyz3KghziNKXlN4oGMSTieuRQKPhtPQC6gJ7LhsD0Bt8/757/oN4MssJzmD2CFC7qNnpcFf28SwVTk9QPuwBPej17+TCEm9r9FdaHDTMAC1utJlZaGFltWN9y3VfDiSLuOYmk9kvzPUHJoVTdc7rMlxn08DCHVlk6f5gs95TKLHHXSW8lUcCB8O0eg+vXCCw9SC9fgqFUTfOdGKytW4VqFpSiNQpdf67J0GRTKu1jj0ioR9BeQR4Y60ALKU43J/yFmBdBbWr2RElqf8B21EXppgbC9gQ1lHW3UvX+QcVUKnJhy6hhFfA2TBgoBC72o8OHRuGjL3EjFbEvvD0nFUKhpm1xqi3UVNKtxDQ2Icw3bxM7amI9u0dTa6Sz/+lKcUf5YkQ4kYPkXMQ5c2R35d5eXVFK1uvkX4b+X51CRMAV4WILWBYJKXEiGkLEZbi0B9hAuYlsOPODHodyKiraKiMy4eoqkNs3SI/T+g6CA5aGbFeptatcZ3QzsUnz3TDW9M8ekoWXYb5nSkyTqU7aza0zI96dagyujj5zSo8xXCmvbUY3HA4nmGY1fL7/B7mVFVg/9bzGmZrZsffp9W85/+FbADxRkpRVFxvCXT8IriqFCl/zhEVw39udTmmkCjmOBcvXgzLXgj10eyKhVu5vz60D2laEd0Tw3rVvdOqJpfkUHryXduRt3qaKs5dotZuUXWrS1L7LurBpmzBUnwcbmZ44miQdKUrzMDK218lX8L+MqL6DcBrRNZBWSfsRT6zDOWM46BmcfzHM/PWiK2IjJT533InfZYrcnHlGMRUKwCIVgnamE3Dj7gxHQ/N9aohSuJ3S0H2wsnZ9g1jY+R/ghLHANZrcCusmDRbyrsRRxz34k9NYlDnZE9mdqO57qO745M6jquQ0eeZTuWZXkjEwICmSwzxC0EC4lBXkazDBhvAGVgAfuzNRp8Rmpd5uGSYQIDPs5WLaGfSfyUZ9WXOst/TpTw7rmkL1gkDGzDpUQGexfydDnIEuqzkPk4PCVxloA17yC0OFq+E6f2BKyXdBBklYOVKqzJ6VU7QMWHTOM3bHwcYD3uZOcsL0lIFKr3Ik61TkNXOkplnWxNpqk8dO1ctpezbuUkO3HSdMPyfyPYGMnW18qbZ5oywFhcqdC5AhoH94VPUlVYgDxBNUD278jlK5/6LsrqtsyW+qQ/dtLRH6iWbkV31RC9xdh3xJuWavNNuCXPrA7nl78VyMPaCfik//mDikvvofRzt2c/oN6CQA4ycUPD2BWO3tFJ8wOPs9USYybzl9Xy08tfilT5cKBQxgnr9doQYYEhgoKA+xh8fYdbKcWeBySBgjf0vCwkqF1vaT3tvyavVtlMzHMU+GtvXs0hKoJApPOK8akF417qxWqhiliXTOHygkvTzUQR3AB9TxS1HOkM2yk6lrFTlaDKvFxDUCcIx0E4daht+67lD6SVuEVUpPfKovcRH7yFPcHbN/TEhdhd4YnFUNgUwVGqQ8IhTwQjVc78jIBKfq8qS+pCtKxxlZdmG/0Pd4eHAoGdoJQ7uUh2+jkMP3jA6RpjUV+xp5YzdifaGvSMDQKWgiWAnJsg/nTjL0gMYgImIoSvTy+//hNvWkNaDifSmdFVWfjNdvhozAHp0jWWx7h6B18/6DWxOIbv2k6rdSToZGfamnUGmv3Bgqu4cB6ElXEw0f5CJdtLhtrRP2an0Fz0vEBOrruAMEF8d/B8WytpiIG7NtKyOoc8aZDthGwjTqod5JkP3qw37yaI2qHgpeu9fYlBKYNIXKcADaWJKI5WOV+j+CnxVE+3QM5iAgXHVat3IRcMQ+jrURqfnTwdTYqWkO8Ip31qXiR9a9XL93RNH2qqp+VHKv7sMu5Rh7cdBz7VYkQRBJVBzq5ZliqwsR8V9Rf7tDnwg6FPER+9dejjWPI4QcUy8o5oV7GMPfaoHzim401GxKb+YNqHPKhnjTwUcW6tFPWmYa+qD1R5r5ePt6g/NbmhzEDv1cIW9WxkIX5E4AjHr3Gk09vO3Sccl5O+p0I5XGdvpf3UnOxT/7Bx7JYllx2XFzrUPZOgufunZWHcdn+YtR//BA==
+
+ + +## 移植 + +第一步是设置构建系统。假设您要移植 Frida 以在 Linux/MIPS 上运行。由于 Frida 已经支持 Linux,我们需要做的就是添加特定于架构的部分。 + +### 移植构建系统 + +根据架构,您可能需要调整 `releng/machine_spec.py`。查看 `build/` 中生成的机器文件,例如 build/frida-linux-mips.txt,以确保工具链配置正确。 + +### 构建 frida-gum + +这是最低级别的组件,也是通常需要大部分移植工作的地方。要构建它,请运行: + +{% highlight bash %} +$ git clone https://github.com/frida/frida-gum.git +$ cd frida-gum +$ make +{% endhighlight %} + +这可能不会成功,但至少应该设置好环境。 + +一旦您让代码编译通过,就该专注于让测试通过了: + +{% highlight bash %} +$ make test +{% endhighlight %} + +您也可以运行单个测试,例如 + +{% highlight bash %} +$ FRIDA_TEST_OPTIONS="--test-args='-p /Core/Process/process_modules' -v" make test +{% endhighlight %} + +### 移植 frida-gum + +通过复制例如 [gum/backend-arm64](https://github.com/frida/frida-gum/tree/main/gum/backend-arm64) 来添加目录 *gum/backend-mips*,然后搜索替换所有内容。这里要移植的重要部分是 *guminterceptor-mips.c* 和 *gumspinlock-mips.c*。您应该将 *gumstalker-mips.c* 留作存根,因为它是一个高级功能,需要大量精力来移植。 + +### 构建 frida-core + +现在 frida-gum 可以工作了,是时候为 frida-core 重复相同的过程了。 + +### 移植 frida-core + +这应该只是移植注入器的问题。实现位于 [此处](https://github.com/frida/frida-core/blob/main/src/linux/frida-helper-backend.vala),推荐的方法是按照 `#if X86` 线索来移植特定于架构的部分。有关 Linux 注入器的演练,请查看我们 [此处](https://www.youtube.com/watch?v=uc1mbN9EJKQ) 的演示。 diff --git a/_i18n/cn/_docs/history.md b/_i18n/cn/_docs/history.md new file mode 100644 index 00000000..0e8584c6 --- /dev/null +++ b/_i18n/cn/_docs/history.md @@ -0,0 +1,24 @@ +Frida 诞生于 [@oleavr][] 和 [@hsorbo][] 随意头脑风暴之后,他们希望能够将繁琐的手动逆向工程转变为更有趣、更高效和更具交互性的工作。 + +在构建了 [oSpy][] 和其他自定义工具来解决逆向工程的痛点之后,[@oleavr][] 开始拼凑 [frida-gum][],这是一个通用的跨平台 C 代码插桩库。当时它仅限于 hook 函数并提供一些工具来帮助开发人员编写针对内存泄漏的单元测试和在极细粒度级别上进行分析。后来它被进一步改进并用于创建 Frida。组件 [frida-core][] 将负责将共享库注入任意进程的所有细节,并与在该进程内运行的注入代码保持实时的双向通道。在该 payload 内部,[frida-gum][] 将负责 hook 函数并使用优秀的 [QuickJS][] 引擎提供脚本运行时。 + +后来,在他们并不充裕的业余时间里,[@oleavr][] 和 [@karltk][] 进行了一些娱乐性的结对编程黑客马拉松,这导致了 [frida-gum][] 的代码跟踪引擎(即所谓的 [Stalker][])的 [巨大改进][huge improvements]。还创建了 Python 绑定。他们开始意识到是时候让外面的人知道这个项目了,因此进一步的黑客马拉松致力于拼凑一个网站和一些急需的文档。 + +今天,对于任何对动态插桩和/或逆向工程感兴趣的人来说,Frida 应该是一个非常有用的工具箱。现在有 [Node.js][]、[Python][]、[Swift][]、[.NET][]、[Qt/Qml][]、[Go][] 的语言绑定,也可以从 C 使用 Frida。 + + +[@oleavr]: https://twitter.com/oleavr +[@hsorbo]: https://twitter.com/hsorbo +[@karltk]: https://twitter.com/karltk +[frida-core]: https://github.com/frida/frida-core +[frida-gum]: https://github.com/frida/frida-gum +[Stalker]: https://github.com/frida/frida-gum/blob/master/gum/backend-x86/gumstalker-x86.c +[huge improvements]: http://blog.kalleberg.org/post/833101026/live-x86-code-instrumentation-with-frida +[Node.js]: https://github.com/frida/frida-node +[Python]: https://github.com/frida/frida-python +[Swift]: https://github.com/frida/frida-swift +[.NET]: https://github.com/frida/frida-clr +[Qt/Qml]: https://github.com/frida/frida-qml +[Go]: https://github.com/frida/frida-go +[oSpy]: https://github.com/oleavr/ospy +[QuickJS]: https://bellard.org/quickjs/ diff --git a/_i18n/cn/_docs/index.md b/_i18n/cn/_docs/index.md new file mode 100644 index 00000000..7ff14f3d --- /dev/null +++ b/_i18n/cn/_docs/index.md @@ -0,0 +1,43 @@ +本网站旨在成为 Frida 的综合指南。我们将涵盖诸如从命令行进行交互式函数跟踪、在 Frida 的 API 之上构建自己的工具等主题,并为您参与 Frida 本身的未来开发提供一些建议。 + +## 那么 Frida 到底是什么? + +它是原生应用的 [Greasemonkey](https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/),或者用更专业的术语来说,它是一个动态代码插桩工具包。它允许您将 JavaScript 片段或您自己的库注入到 Windows、macOS、GNU/Linux、iOS、watchOS、tvOS、Android、FreeBSD 和 QNX 上的原生应用中。Frida 还为您提供了一些基于 Frida API 构建的简单工具。这些工具可以直接使用,根据您的需要进行调整,或者作为如何使用 API 的示例。 + +## 为什么我需要这个? + +好问题。我们将尝试通过一些用例来阐明: + +- 有一个新的热门应用大家都非常兴奋,但它只在 iOS 上可用,而您很想与它进行互操作。您意识到它依赖于加密的网络协议,像 Wireshark 这样的工具无法解决问题。您拿起 Frida 并使用它进行 API 跟踪。 +- 您正在构建一个桌面应用,该应用已部署在客户现场。出现了一个问题,但内置的日志代码不够用。您需要向客户发送一个包含大量昂贵日志代码的自定义构建版本。然后您意识到您可以只使用 Frida 并构建一个特定于应用的工具,该工具将添加您需要的所有诊断信息,而且只需几行 Python 代码。无需向客户发送新的自定义构建版本 - 您只需发送该工具,它将在您应用的许多版本上工作。 +- 您想构建一个增强版的 Wireshark,支持嗅探加密协议。它甚至可以操纵函数调用来伪造网络条件,否则这需要您建立一个测试实验室。 +- 您的内部应用可以使用一些黑盒测试,而无需用仅用于奇异测试的逻辑污染您的生产代码。 + +## 为什么是 Python API,但调试逻辑却是 JavaScript? + +Frida 的核心是用 C 编写的,并将 [QuickJS](https://bellard.org/quickjs/) 注入到目标进程中,在那里您的 JS 可以完全访问内存、hook 函数甚至调用进程内的原生函数。有一个双向通信通道,用于在您的应用和在目标进程内运行的 JS 之间进行对话。 + +使用 Python 和 JS 允许使用无风险的 API 进行快速开发。Frida 可以帮助您轻松捕获 JS 中的错误并为您提供异常而不是崩溃。 + +不想用 Python 写?没问题。您可以直接从 C 使用 Frida,在这个 C 核心之上还有多种语言绑定,例如 [Node.js](https://github.com/frida/frida-node)、[Python](https://github.com/frida/frida-python)、[Swift](https://github.com/frida/frida-swift)、[.NET](https://github.com/frida/frida-clr)、[Qml](https://github.com/frida/frida-qml)、[Go](https://github.com/frida/frida-go) 等。为其他语言和环境构建额外的绑定非常容易。 + +## ProTips™、注意和警告 + +在本指南中,有许多小而有用的信息,可以使使用 Frida 更容易、更有趣且更少危险。以下是需要注意的事项。 + +
+
ProTips™ 帮助您从 Frida 获得更多
+

这些提示和技巧将帮助您成为 Frida 向导!

+
+ +
+
注意是有用的信息片段
+

这些是理解 Frida 有时需要的额外信息。

+
+ +
+
警告帮助您不要搞砸事情
+

如果您希望避免某些死亡,请注意这些消息。

+
+ +如果您在途中遇到任何我们未涵盖的内容,或者如果您知道您认为其他人会觉得有用的提示,请[提交 issue]({{ site.organization_url }}/frida-website/issues/new),我们将考虑将其包含在本指南中。 diff --git a/_i18n/cn/_docs/installation.md b/_i18n/cn/_docs/installation.md new file mode 100644 index 00000000..5a302c26 --- /dev/null +++ b/_i18n/cn/_docs/installation.md @@ -0,0 +1,84 @@ +安装 Frida 并准备就绪只需要几分钟。如果它变得很麻烦,请[提交 issue]({{ site.organization_url }}/frida-website/issues/new)(或提交 pull request)描述您遇到的问题以及我们如何使该过程更容易。 + +### Frida CLI 工具的要求 + +安装 Frida 的 CLI 工具简单直接,但在开始之前,您需要确保您的系统满足一些要求。 + +- [Python](https://python.org/) – 强烈推荐最新的 3.x 版本 +- Windows, macOS, or GNU/Linux + +## 使用 pip 安装 + +安装 Frida CLI 工具的最好方法是通过 [PyPI][]: + +{% highlight bash %} +$ pip install frida-tools +{% endhighlight %} + +如果您在安装 Frida 时遇到问题,请查看[故障排除][troubleshooting]页面或[报告问题]({{ site.organization_url }}/frida-website/issues/new),以便 Frida 社区可以为每个人改善体验。 + +## 手动安装 + +您也可以从 Frida 的 GitHub [releases][] 页面获取其他二进制文件。 + +## 测试您的安装 + +启动一个我们可以注入的进程: + +{% highlight bash %} +$ cat +{% endhighlight %} + +让它静置并等待输入。在 Windows 上,您可能想使用 `notepad.exe`。 + +请注意,此示例在 macOS El Capitan 及更高版本上不起作用,因为它拒绝针对系统二进制文件的此类尝试。有关更多详细信息,请参阅[此处]({{ site.repository }}/issues/83)。但是,如果您将 `cat` 二进制文件复制到例如 `/tmp/cat`,然后运行它,则该示例应该可以工作: + +{% highlight bash %} +$ cp /bin/cat /tmp/cat +$ /tmp/cat +{% endhighlight %} + +在另一个终端中,创建一个包含以下内容的 `example.py` 文件: + +{% highlight py %} +import frida + +def on_message(message, data): + print("[on_message] message:", message, "data:", data) + +session = frida.attach("cat") + +script = session.create_script(""" +rpc.exports.enumerateModules = () => { + return Process.enumerateModules(); +}; +""") +script.on("message", on_message) +script.load() + +print([m["name"] for m in script.exports_sync.enumerate_modules()]) +{% endhighlight %} + +如果您在 GNU/Linux 上,请执行: + +{% highlight bash %} +$ sudo sysctl kernel.yama.ptrace_scope=0 +{% endhighlight %} + +以启用对非子进程的 ptrace。 + +此时,我们准备好试用 Frida 了!运行 example.py 脚本并观看奇迹发生: + +{% highlight bash %} +$ python example.py +{% endhighlight %} + +输出应该类似于此(取决于您的平台和库版本): + +{% highlight py %} +['cat', …, 'ld-2.15.so'] +{% endhighlight %} + +[PyPI]: https://pypi.python.org/pypi/frida-tools +[troubleshooting]: ../troubleshooting/ +[releases]: https://github.com/frida/frida/releases diff --git a/_i18n/cn/_docs/ios.md b/_i18n/cn/_docs/ios.md new file mode 100644 index 00000000..c3a9a406 --- /dev/null +++ b/_i18n/cn/_docs/ios.md @@ -0,0 +1,91 @@ +Frida 支持两种操作模式,具体取决于您的 iOS 设备是否已越狱。 + +## 目录 + 1. [已越狱](#已越狱) + 1. [未越狱](#未越狱) + +## 已越狱 + +这是最强大的设置,因为它允许您以极少的努力对系统服务和应用程序进行插桩。 + +在本教程中,我们将向您展示如何在 iOS 设备上进行函数跟踪。 + +### 设置您的 iOS 设备 + +启动 `Cydia` 并通过转到 `软件源` -> `编辑` -> `添加` 并输入 `https://build.frida.re` 来添加 Frida 的仓库。您现在应该能够找到并安装 `Frida` 包,它允许 Frida 将 JavaScript 注入到 iOS 设备上运行的应用程序中。这是通过 USB 进行的,因此您需要准备好 USB 数据线,尽管还不需要将其插入。 + +### 快速冒烟测试 + +现在,回到您的 Windows 或 macOS 系统,是时候确保基础功能正常工作了。运行: + +{% highlight bash %} +$ frida-ps -U +{% endhighlight %} + +
+
使用基于 Linux 的操作系统?
+

+ 从 Frida 6.0.9 开始,现在有了 usbmuxd 集成,所以 -U 可以工作。 + 对于较早的 Frida 版本,您可以使用 WiFi 并在两端的 localhost:27042 之间建立 SSH 隧道,然后使用 -R 代替 -U。 +

+
+ +除非您已经插入了设备,否则您应该看到以下消息: + +{% highlight text %} +Waiting for USB device to appear... +{% endhighlight %} + +插入您的设备,您应该看到一个类似于以下的进程列表: + +{% highlight bash %} + PID NAME + 488 Clock + 116 Facebook + 312 IRCCloud +1711 LinkedIn +… +{% endhighlight %} + +太棒了,我们可以开始了! + +### 跟踪 Twitter 应用中的加密调用 + +好了,让我们找点乐子。在您的设备上启动 Twitter 应用,并在确保它保持在前台且设备未进入睡眠状态的同时,返回桌面并运行: + +{% highlight bash %} +$ frida-trace -U -i "CCCryptorCreate*" Twitter +Uploading data... +CCCryptorCreate: Auto-generated handler …/CCCryptorCreate.js +CCCryptorCreateFromData: Auto-generated handler …/CCCryptorCreateFromData.js +CCCryptorCreateWithMode: Auto-generated handler …/CCCryptorCreateWithMode.js +CCCryptorCreateFromDataWithMode: Auto-generated handler …/CCCryptorCreateFromDataWithMode.js +Started tracing 4 functions. Press Ctrl+C to stop. +{% endhighlight %} + +现在,`CCryptorCreate` 及其朋友是 Apple 的 `libcommonCrypt.dylib` 的一部分,许多应用程序使用它来处理加密、解密、哈希等。 + +重新加载您的 Twitter feed 或以某种导致网络流量的方式操作 UI,您应该看到类似以下的输出: + +{% highlight bash %} +3979 ms CCCryptorCreate() +3982 ms CCCryptorCreateWithMode() +3983 ms CCCryptorCreate() +3983 ms CCCryptorCreateWithMode() +{% endhighlight %} + +您现在可以在阅读 `man CCryptorCreate` 时实时编辑上述 JavaScript 文件,并开始越来越深入地研究您的 iOS 应用。 + +## 未越狱 + +Frida 能够对可调试的应用程序进行插桩,并且从 Frida 12.7.12 开始会自动注入 [Gadget](/docs/gadget/)。 + +只有几个要求需要注意: + +- iOS 设备最好运行 iOS 13 或更高版本。对旧版本的支持被认为是实验性的。 +- 必须挂载开发者磁盘镜像。Xcode 一旦发现 iOS USB 设备就会自动挂载它,但您也可以使用 *ideviceimagemounter* 手动挂载。 +- 最新的 Gadget 必须存在于用户的缓存目录中。在 macOS 上,这是 `~/.cache/frida/gadget-ios.dylib`,但您可以通过尝试附加到可调试应用程序然后读取错误消息来找出确切路径。 + +## 构建您自己的工具 + +虽然像 *frida*、*frida-trace* 等 CLI 工具绝对非常有用,但有时您可能希望利用强大的 [Frida API](/docs/javascript-api/) 构建自己的工具。为此,我们建议阅读有关 [Functions](/docs/functions) 和 [Messages](/docs/messages) 的章节,并且在任何看到 `frida.attach()` 的地方,只需将其替换为 `frida.get_usb_device().attach()`。 diff --git a/_i18n/cn/_docs/javascript-api.md b/_i18n/cn/_docs/javascript-api.md new file mode 100644 index 00000000..7dbb33e7 --- /dev/null +++ b/_i18n/cn/_docs/javascript-api.md @@ -0,0 +1,4152 @@ +## 入门指南 + +为了提高效率,我们强烈建议使用我们的 **[TypeScript](https://www.typescriptlang.org/)** +绑定。这意味着你可以获得代码补全、类型检查、内联文档、重构工具等功能。 + +这里有一个简短的预告视频展示了编辑体验: + +[![Frida TypeScript demo](https://i.ytimg.com/vi/9cr4gOPFN4o/sddefault.jpg)](https://youtu.be/9cr4gOPFN4o) + +克隆 **[这个仓库](https://github.com/oleavr/frida-agent-example)** 即可开始。 + +## 目录 + +1. **运行时信息** + 1. [Frida](#frida) + 1. [Script](#script) +1. **进程、线程、模块和内存** + 1. [Thread](#thread) + 1. [Process](#process) + 1. [Module](#module) + 1. [ModuleMap](#modulemap) + 1. [Memory](#memory) + 1. [MemoryAccessMonitor](#memoryaccessmonitor) + 1. [CModule](#cmodule) + 1. [RustModule](#rustmodule) + 1. [ApiResolver](#apiresolver) + 1. [DebugSymbol](#debugsymbol) + 1. [Kernel](#kernel) +1. **数据类型、函数和回调** + 1. [Int64](#int64) + 1. [UInt64](#uint64) + 1. [NativePointer](#nativepointer) + 1. [ArrayBuffer](#arraybuffer) + 1. [NativeFunction](#nativefunction) + 1. [NativeCallback](#nativecallback) + 1. [SystemFunction](#systemfunction) +1. **网络** + 1. [Socket](#socket) + 1. [SocketListener](#socketlistener) + 1. [SocketConnection](#socketconnection) +1. **文件和流** + 1. [File](#file) + 1. [IOStream](#iostream) + 1. [InputStream](#inputstream) + 1. [OutputStream](#outputstream) + 1. [UnixInputStream](#unixinputstream) + 1. [UnixOutputStream](#unixoutputstream) + 1. [Win32InputStream](#win32inputstream) + 1. [Win32OutputStream](#win32outputstream) +1. **数据库** + 1. [SqliteDatabase](#sqlitedatabase) + 1. [SqliteStatement](#sqlitestatement) +1. **插桩** + 1. [Interceptor](#interceptor) + 1. [Stalker](#stalker) + 1. [ObjC](#objc) + 1. [Java](#java) +1. **CPU 指令** + 1. [Instruction](#instruction) + 1. [X86Writer](#x86writer) + 1. [X86Relocator](#x86relocator) + 1. [x86 enum types](#x86-enum-types) + 1. [ArmWriter](#armwriter) + 1. [ArmRelocator](#armrelocator) + 1. [ThumbWriter](#thumbwriter) + 1. [ThumbRelocator](#thumbrelocator) + 1. [ARM enum types](#arm-enum-types) + 1. [Arm64Writer](#arm64writer) + 1. [Arm64Relocator](#arm64relocator) + 1. [AArch64 enum types](#aarch64-enum-types) + 1. [MipsWriter](#mipswriter) + 1. [MipsRelocator](#mipsrelocator) + 1. [MIPS enum types](#mips-enum-types) +1. **其他** + 1. [Console](#console) + 1. [Hexdump](#hexdump) + 1. [Shorthand](#shorthand) + 1. [Communication between host and injected process](#communication-between-host-and-injected-process) + 1. [Timing events](#timing-events) + 1. [Garbage collection](#garbage-collection) + 1. [Worker](#worker) + 1. [Cloak](#cloak) + 1. [Profiler](#profiler) + 1. [Sampler](#sampler) + 1. [CycleSampler](#cycle-sampler) + 1. [BusyCycleSampler](#busy-cycle-sampler) + 1. [WallClockSampler](#wall-clock-sampler) + 1. [UserTimeSampler](#user-time-sampler) + 1. [MallocCountSampler](#malloc-count-sampler) + 1. [CallCountSampler](#call-count-sampler) + +--- + +## 运行时信息 + +### Frida + ++ `Frida.version`: 包含当前 Frida 版本的字符串属性。 + ++ `Frida.heapSize`: 包含 Frida 私有堆当前大小的动态属性,该堆由所有脚本和 Frida 自身的运行时共享。 + 这对于监视你的插桩代码在宿主进程消耗的总内存中使用了多少内存非常有用。 + +### Script + ++ `Script.runtime`: 包含正在使用的运行时的字符串属性。 + 要么是 `QJS`,要么是 `V8`。 + ++ `Script.evaluate(name, source)`: 在全局作用域中评估给定的 JavaScript 字符串 `source`, + 其中 `name` 是指定脚本名称的字符串,例如 `/plugins/tty.js`。提供的名称是用于未来堆栈跟踪的 UNIX 风格虚拟文件系统路径。 + {: #script-evaluate} + + 对于想要在自己的脚本中支持加载用户提供的脚本的代理非常有用。相比简单地使用 `eval()`,它的两个好处是 + 可以提供脚本文件名,并且支持源映射——包括内联的和通过 [`Script.registerSourceMap()`](#script-registersourcemap) 提供的。 + + 返回评估代码的结果值。 + ++ `Script.load(name, source)`: 将给定的 JavaScript 字符串 `source` 编译并评估为 ES 模块, + 其中 `name` 是指定模块名称的字符串,例如 `/plugins/screenshot.js`。提供的名称是用于未来堆栈跟踪的 UNIX 风格虚拟文件系统路径, + 并且对其他模块可见,这些模块可以静态或动态地导入它。 + + 对于想要在自己的脚本中支持加载用户提供的脚本的代理非常有用。此 API 提供了与 [`Script.evaluate()`](#script-evaluate) + 相比 `eval()` 相同的好处,此外还将用户提供的代码封装在其自己的 ES 模块中。这意味着可以导出值, + 随后由其他模块导入。父脚本也可以导出可以从加载的子脚本导入的值。 + 这要求父脚本使用较新版本的 frida-compile 使用的新 ES 模块捆绑格式。 + + 返回一个解析为模块命名空间对象的 *Promise*。 + ++ `Script.registerSourceMap(name, json)`: 为指定的脚本 `name` 注册源映射, + `name` 是一个 UNIX 风格的虚拟文件系统路径字符串,例如 `/plugins/screenshot.js`。 + 源映射 `json` 是包含源映射的原始 JSON 表示的字符串。 + {: #script-registersourcemap} + + 理想情况下应在加载给定脚本之前调用,以便加载期间创建的堆栈跟踪可以使用源映射。 + ++ `Script.nextTick(func[, ...params])`: 在下一个 tick 运行 `func`,即 + 当当前本机线程退出 JavaScript 运行时时。任何额外的 `params` 都会传递给它。 + ++ `Script.pin()`: 暂时阻止当前脚本被卸载。 + 这是引用计数的,因此必须在稍后有一个匹配的 *unpin()*。通常在 *bindWeak()* 的回调中使用, + 当你需要在另一个线程上安排清理时。 + ++ `Script.unpin()`: 撤销之前的 *pin()*,以便可以卸载当前脚本。 + ++ `Script.bindWeak(value, fn)`: 监视 `value`,并在 `value` 被垃圾回收或脚本即将被卸载时 + 立即调用 `fn` 回调。返回一个 ID,你可以将其传递给 [`Script.unbindWeak()`](#unbindweak) 进行显式清理。 + {: #bindweak} + + 如果你正在构建语言绑定,并且需要在不再需要 JS 值时释放本机资源,则此 API 非常有用。 + ++ `Script.unbindWeak(id)`: 停止监视传递给 `Script.bindWeak(value, fn)` 的值, + 并立即调用 `fn` 回调。 + {: #unbindweak} + ++ `Script.setGlobalAccessHandler(handler | null)`: 安装或卸载用于解决访问不存在的全局变量的尝试的处理程序。 + 对于实现 REPL 非常有用,其中未知的标识符可以从数据库中延迟获取。 + + `handler` 是一个包含两个属性的对象: + + - `enumerate()`: 查询存在哪些额外的全局变量。必须返回一个字符串数组。 + - `get(property)`: 检索给定属性的值。 + +--- + +## 进程、线程、模块和内存 + +### Process + ++ `Process.id`: 包含 PID 的数字属性 + ++ `Process.arch`: 包含字符串 `ia32`、`x64`、`arm` 或 `arm64` 的属性 + {: #process-arch} + ++ `Process.platform`: 包含字符串 `windows`、`darwin`、`linux`、`freebsd`、`qnx` 或 `barebone` 的属性 + ++ `Process.pageSize`: 包含虚拟内存页面大小(以字节为单位)的数字属性。 + 这用于使你的脚本更具可移植性。 + {: #process-pagesize} + ++ `Process.pointerSize`: 包含指针大小(以字节为单位)的数字属性。 + 这用于使你的脚本更具可移植性。 + {: #process-pointersize} + ++ `Process.codeSigningPolicy`: 包含字符串 `optional` 或 `required` 的属性, + 后者意味着 Frida 将避免修改内存中的现有代码,并且不会尝试运行未签名的代码。 + 目前,除非你使用 **[Gadget](/docs/gadget)** 并将其配置为假设需要代码签名,否则此属性将始终设置为 `optional`。 + 此属性允许你确定 **[Interceptor](#interceptor)** API 是否受限,以及修改代码或运行未签名代码是否安全。 + ++ `Process.mainModule`: 包含表示进程主可执行文件的 [`Module`](#module) 的属性 + ++ `Process.getCurrentDir()`: 返回指定当前工作目录的文件系统路径的字符串 + ++ `Process.getHomeDir()`: 返回指定当前用户主目录的文件系统路径的字符串 + ++ `Process.getTmpDir()`: 返回指定用于临时文件的目录的文件系统路径的字符串 + ++ `Process.isDebuggerAttached()`: 返回一个布尔值,指示当前是否附加了调试器 + ++ `Process.getCurrentThreadId()`: 获取此线程的操作系统特定 ID(数字) + ++ `Process.enumerateThreads()`: 枚举正在运行的线程,返回 **[Thread](#thread)** 对象的数组。 + {: #process-enumeratethreads} + ++ `Process.attachThreadObserver(callbacks)`: 开始观察线程,在添加、移除和重命名线程时调用提供的 `callbacks`。 + + `callbacks` 参数是一个包含以下一个或多个属性的对象: + + - `onAdded(thread)`: 给定刚刚添加的 [`Thread`](#thread) 的回调函数。 + 立即使用所有现有线程调用,因此可以轻松管理初始状态与更新,而无需担心竞争条件。 + 当使用全新的线程调用时,调用从该新线程同步发生。 + + - `onRemoved(thread)`: 给定刚刚移除(即即将终止)的 [`Thread`](#thread) 的回调函数。 + 调用从即将终止的线程同步发生。 + + - `onRenamed(thread, previousName)`: 给定刚刚重命名的 [`Thread`](#thread) 的回调函数, + 带有其新的 `name` 属性,以及第二个参数 `previousName` 指定其先前的名称。 + 先前的名称是字符串,如果线程以前未命名,则为 `null`。 + + 请注意,[`Thread`](#thread) 对象缺少 `state` 和 `context` 属性,因为这些属性本质上是高度易变的, + 并且不会观察到它们的变化。请注意,你可以将此 API 与 [`Stalker`](#stalker) 结合使用以跟踪单个线程的执行。 + + 返回一个你可以调用 `detach()` 的观察者对象。 + ++ `Process.runOnThread(id, callback)`: 在由 `id` 指定的线程上运行 JavaScript 函数 `callback`, + 不带任何参数。返回一个 *Promise*,该 Promise 接收你的回调返回的值。 + + 由于线程可能会在不可重入代码中被中断,因此必须非常谨慎地使用。例如,你可能会在它处于某些微妙代码中间、 + 持有特定的非递归锁时中断它,然后当你调用某些函数时尝试再次隐式获取该锁。 + ++ `Process.findModuleByAddress(address)`, + `Process.getModuleByAddress(address)`, + `Process.findModuleByName(name)`, + `Process.getModuleByName(name)`: + 返回一个 **[Module](#module)**,其 *address* 或 *name* 与指定的匹配。 + 如果找不到此类模块,*find*-前缀函数返回 *null*,而 *get*-前缀函数抛出异常。 + {: #process-getmodulebyname} + ++ `Process.enumerateModules()`: 枚举当前加载的模块,返回 **[Module](#module)** 对象的数组。 + {: #process-enumeratemodules} + ++ `Process.attachModuleObserver(callbacks)`: 开始观察模块,在添加和移除模块时调用提供的 `callbacks`。 + + `callbacks` 参数是一个包含以下一个或多个属性的对象: + + - `onAdded(module)`: 给定刚刚添加的 [`Module`](#module) 的回调函数。 + 立即使用所有现有模块调用,因此可以轻松管理初始状态与更新,而无需担心竞争条件。 + 当使用全新模块调用时,调用在该模块加载后立即同步发生,但在应用程序有机会使用它之前。 + 这意味着这是应用插桩的好时机,例如使用 [`Interceptor`](#interceptor)。 + + - `onRemoved(module)`: 给定刚刚移除(即卸载)的 [`Module`](#module) 的回调函数。 + + 返回一个你可以调用 `detach()` 的观察者对象。 + ++ `Process.findRangeByAddress(address)`, `getRangeByAddress(address)`: + 返回一个包含有关包含 *address* 的范围的详细信息的对象。 + 如果找不到此类范围,*findRangeByAddress()* 返回 *null*,而 *getRangeByAddress()* 抛出异常。 + 有关包含哪些字段的详细信息,请参阅 [`Process.enumerateRanges()`](#process-enumerateranges)。 + ++ `Process.enumerateRanges(protection|specifier)`: 枚举满足给定 `protection` 字符串(形式为 `rwx`, + 其中 `rw-` 表示“必须至少可读且可写”)的内存范围。或者,你可以提供一个 `specifier` 对象, + 其中包含一个 `protection` 键(其值如前所述)和一个 `coalesce` 键(设置为 `true`, + 如果你希望合并具有相同保护的相邻范围(默认为 `false`;即保持范围分开))。 + 返回包含以下属性的对象数组: + {: #process-enumerateranges} + + - `base`: [`NativePointer`](#nativepointer) 形式的基地址 + - `size`: 字节大小 + - `protection`: 保护字符串(见上文) + - `file`: (如果可用)文件映射详细信息对象,包含: + + - `path`: 完整文件系统路径字符串 + - `offset`: 磁盘上映射文件的偏移量(以字节为单位) + - `size`: 磁盘上映射文件的大小(以字节为单位) + ++ `Process.enumerateMallocRanges()`: 就像 [`enumerateRanges()`](#process-enumerateranges), + 但针对系统堆已知的单个内存分配。 + ++ `Process.setExceptionHandler(callback)`: 安装进程范围的异常处理程序回调, + 该回调有机会在宿主进程本身处理之前处理本机异常。使用单个参数 `details` 调用,该对象包含: + {: #process-setexceptionhandler} + + - `type`: 指定以下之一的字符串: + * abort + * access-violation + * guard-page + * illegal-instruction + * stack-overflow + * arithmetic + * breakpoint + * single-step + * system + - `address`: 发生异常的地址,作为 **[NativePointer](#nativepointer)** + - `memory`: 如果存在,是一个包含以下内容的对象: + - `operation`: 触发异常的操作类型,指定为 `read`、`write` 或 `execute` 的字符串 + - `address`: 发生异常时访问的地址,作为 **[NativePointer](#nativepointer)** + - `context`: 具有键 `pc` and `sp` 的对象,它们是指定 EIP/RIP/PC 和 ESP/RSP/SP 的 + **[NativePointer](#nativepointer)** 对象,分别用于 ia32/x64/arm。 + 其他特定于处理器的键也可用,例如 `eax`, `rax`, `r0`, `x0` 等。 + 你也可以通过分配给这些键来更新寄存器值。 + - `nativeContext`: 操作系统和架构特定的 CPU 上下文结构的地址,作为 **[NativePointer](#nativepointer)**。 + 这仅作为最后的手段暴露,用于 `context` 无法提供足够细节的边缘情况。 + 但是,我们要劝阻使用此方法,而是提交拉取请求以添加你的用例所需的缺失位。 + + 由你的回调决定如何处理异常。它可以记录问题,通过 **[send()](#communication-send)** 通知你的应用程序, + 然后进行阻塞 recv() 以确认发送的数据已被接收,或者它可以修改寄存器和内存以从异常中恢复。 + 如果你确实处理了异常,则应返回 `true`,在这种情况下,Frida 将立即恢复线程。 + 如果你不返回 `true`,Frida 将把异常转发给宿主进程的异常处理程序(如果有),或者让操作系统终止进程。 + + +### Thread + +由例如 [`Process.enumerateThreads()`](#process-enumeratethreads) 返回的对象。

+ +- `id`: 操作系统特定的 ID,作为数字 + +- `name`: 指定线程名称的字符串(如果可用) + +- `state`: 线程状态的快照,作为指定 `running`、`stopped`、`waiting`、`uninterruptible` 或 `halted` 的字符串 + +- `context`: CPU 寄存器的快照,作为一个具有键 `pc` 和 `sp` 的对象,它们是指定 EIP/RIP/PC 和 ESP/RSP/SP 的 + **[NativePointer](#nativepointer)** 对象,分别用于 ia32/x64/arm。 + 其他特定于处理器的键也可用,例如 `eax`, `rax`, `r0`, `x0` 等。 + {: #thread-context} + +- `entrypoint`: 线程开始执行的位置(如果适用且可用)。如果存在,它是一个包含以下内容的对象: + + - `routine`: 线程的启动例程,作为 [`NativePointer`](#nativepointer) + + - `parameter`: 传递给 `routine` 的参数(如果可用),作为 [`NativePointer`](#nativepointer) + +- `setHardwareBreakpoint(id, address)`: 设置硬件断点,其中 `id` 是指定断点 ID 的数字, + `address` 是指定断点地址的 [`NativePointer`](#nativepointer)。 + 通常与 [`Process.setExceptionHandler()`](#process-setexceptionhandler) 结合使用以处理引发的异常。 + {: #thread-sethardwarebreakpoint} + +- `unsetHardwareBreakpoint(id)`: 取消设置硬件断点,其中 `id` 是指定先前通过调用 + [`setHardwareBreakpoint()`](#thread-sethardwarebreakpoint) 设置的断点 ID 的数字。 + +- `setHardwareWatchpoint(id, address, size, conditions)`: 设置硬件观察点, + 其中 `id` 是指定观察点 ID 的数字,`address` 是指定要监视的区域地址的 [`NativePointer`](#nativepointer), + `size` 是指定该区域大小的数字,`conditions` 是指定 `r`、`w` 或 `rw` 的字符串。 + 在这里,`r` 表示监视读取,`w` 表示监视写入,`rw` 表示监视读取和写入。 + 通常与 [`Process.setExceptionHandler()`](#process-setexceptionhandler) 结合使用以处理引发的异常。 + {: #thread-sethardwarewatchpoint} + +- `unsetHardwareWatchpoint(id)`: 取消设置硬件观察点,其中 `id` 是指定先前通过调用 + [`setHardwareWatchpoint()`](#thread-sethardwarewatchpoint) 设置的观察点 ID 的数字。 + ++ `Thread.backtrace([context, backtracer])`: 生成当前线程的回溯,作为 [`NativePointer`](#nativepointer) 对象数组返回。 + {: #thread-backtrace} + + 如果你从 Interceptor 的 [`onEnter`](#interceptor-onenter) 或 [`onLeave`](#interceptor-onleave) 回调中调用此函数, + 你应该为可选的 `context` 参数提供 `this.context`,因为它将为你提供更准确的回溯。 + 省略 `context` 意味着回溯将从当前堆栈位置生成,由于 JavaScript VM 的堆栈帧,这可能无法为你提供非常好的回溯。 + 可选的 `backtracer` 参数指定要使用的回溯器类型,必须是 `Backtracer.FUZZY` 或 `Backtracer.ACCURATE`, + 如果未指定,则后者为默认值。准确的回溯器类型依赖于调试器友好的二进制文件或调试信息的存​​在才能做好工作, + 而模糊回溯器对堆栈执行取证以猜测返回地址,这意味着你会得到误报,但它适用于任何二进制文件。 + 生成的回溯目前限制为 16 帧,并且在不重新编译 Frida 的情况下无法调整。 + +{% highlight js %} +const commonCrypto = Process.getModuleByName('libcommonCrypto.dylib'); +const f = commonCrypto.getExportByName('CCCryptorCreate'); +Interceptor.attach(f, { + onEnter(args) { + console.log('CCCryptorCreate called from:\n' + + Thread.backtrace(this.context, Backtracer.ACCURATE) + .map(DebugSymbol.fromAddress).join('\n') + '\n'); + } +}); +{% endhighlight %} + ++ `Thread.sleep(delay)`: 将当前线程的执行挂起 `delay` 秒(指定为数字)。例如 0.05 表示睡眠 50 毫秒。 + + +### Module + +由例如 [`Module.load()`](#module-load) 和 [`Process.enumerateModules()`](#process-enumeratemodules) 返回的对象。

+ +- `name`: 规范模块名称字符串 + +- `base`: [`NativePointer`](#nativepointer) 形式的基地址 + +- `size`: 字节大小 + +- `path`: 完整文件系统路径字符串 + +- `ensureInitialized()`: 确保已运行模块初始化程序。 + 这在早期插桩期间(即在进程生命周期的早期运行)非常重要,以便能够安全地与 API 交互。 + 一个这样的用例是与给定模块提供的 **[ObjC](#objc)** 类进行交互。 + +- `enumerateImports()`: 枚举模块的导入,返回包含以下属性的对象数组: + + - `type`: 指定 `function` 或 `variable` 的字符串 + - `name`: 导入名称字符串 + - `module`: 模块名称字符串 + - `address`: [`NativePointer`](#nativepointer) 形式的绝对地址 + - `slot`: 存储导入的内存位置,作为 [`NativePointer`](#nativepointer) + + 只有 `name` 字段保证存在于所有导入中。平台特定的后端将尽最大努力解析其他字段, + 甚至超出本机元数据提供的范围,但不能保证它会成功。 + +- `enumerateExports()`: 枚举模块的导出,返回包含以下属性的对象数组: + + - `type`: 指定 `function` 或 `variable` 的字符串 + - `name`: 导出名称字符串 + - `address`: [`NativePointer`](#nativepointer) 形式的绝对地址 + +- `enumerateSymbols()`: 枚举模块的符号,返回包含以下属性的对象数组: + + - `isGlobal`: 指定符号是否全局可见的布尔值 + - `type`: 指定以下之一的字符串: + - unknown + - section + - undefined (Mach-O) + - absolute (Mach-O) + - prebound-undefined (Mach-O) + - indirect (Mach-O) + - object (ELF) + - function (ELF) + - file (ELF) + - common (ELF) + - tls (ELF) + - `section`: 如果存在,是一个包含以下内容的对象: + - `id`: 包含部分索引、段名称(如果适用)和部分名称的字符串——格式与 [r2][] 的部分 ID 相同 + - `protection`: 类似于 [`Process.enumerateRanges()`](#process-enumerateranges) 中的保护 + - `name`: 符号名称字符串 + - `address`: [`NativePointer`](#nativepointer) 形式的绝对地址 + - `size`: 如果存在,指定符号大小(以字节为单位)的数字 + +
+
enumerateSymbols() 仅在 i/macOS 和基于 Linux 的操作系统上可用
+

+ 我们也希望在其他平台上支持这一点,所以如果你觉得这很有用并想提供帮助,请联系我们。 + 根据你的用例,你可能也会发现 **[DebugSymbol](#debugsymbol)** API 就足够了。 +

+
+ +- `enumerateRanges(protection)`: 就像 [`Process.enumerateRanges`](#process-enumerateranges), + 除了它的范围限定于模块。 + +- `enumerateSections()`: 枚举模块的部分,返回包含以下属性的对象数组: + + - `id`: 包含部分索引、段名称(如果适用)和部分名称的字符串——格式与 [r2][] 的部分 ID 相同 + - `name`: 部分名称字符串 + - `address`: [`NativePointer`](#nativepointer) 形式的绝对地址 + - `size`: 字节大小 + +- `enumerateDependencies()`: 枚举模块的依赖项,返回包含以下属性的对象数组: + + - `name`: 模块名称字符串 + - `type`: 指定以下之一的字符串: + - regular + - weak + - reexport + - upward + +- `findExportByName(name)`, + `getExportByName(name)`: 返回名为 `name` 的导出的绝对地址。 + 如果找不到此类导出,*find*-前缀函数返回 *null*,而 *get*-前缀函数抛出异常。 + {: #module-getexportbyname} + +- `findSymbolByName(name)`, + `getSymbolByName(name)`: 返回名为 `name` 的符号的绝对地址。 + 如果找不到此类符号,*find*-前缀函数返回 *null*,而 *get*-前缀函数抛出异常。 + ++ `Module.load(path)`: 从文件系统路径加载指定的模块并返回一个 [`Module`](#module) 对象。 + 如果无法加载指定的模块,则抛出异常。 + {: #module-load} + ++ `Module.findGlobalExportByName(name)`, + `Module.getGlobalExportByName(name)`: 返回名为 `name` 的全局导出的绝对地址。 + 这可能是一个昂贵的搜索,应避免使用。如果找不到此类导出, + *find*-前缀函数返回 *null*,而 *get*-前缀函数抛出异常。 + + +### ModuleMap + ++ `new ModuleMap([filter])`: 创建一个新的模块映射,该映射针对确定给定内存地址属于哪个模块(如果有)进行了优化。 + 创建时获取当前加载模块的快照,可以通过调用 [`update()`](#modulemap-update) 进行刷新。 + `filter` 参数是可选的,允许你传递一个用于过滤模块列表的函数。 + 例如,如果你只关心应用程序本身拥有的模块,这很有用,并允许你快速检查地址是否属于其模块之一。 + `filter` 函数被传递一个 **[Module](#module)** 对象,并且必须为应保留在映射中的每个模块返回 `true`。 + 每次更新映射时,都会为每个加载的模块调用它。 + +- `has(address)`: 检查 `address` 是否属于任何包含的模块,并返回结果作为布尔值 + +- `find(address)`, `get(address)`: 返回一个 **[Module](#module)**,其中包含有关 `address` 所属模块的详细信息。 + 如果找不到此类模块,`find()` 返回 `null`,而 `get()` 抛出异常。 + {: #modulemap-find} + +- `findName(address)`, + `getName(address)`, + `findPath(address)`, + `getPath(address)`: + 就像 [`find()`](#modulemap-find) 和 [`get()`](#modulemap-find),但只返回 `name` 或 `path` 字段, + 这意味着当你不需要其他细节时开销更小。 + +- `update()`: 更新映射。你应该在加载或卸载模块后调用此函数,以避免对过时数据进行操作。 + {: #modulemap-update} + +- `values()`: 返回当前在映射中的 **[Module](#module)** 对象的数组。 + 返回的数组是深层副本,并且在调用 [`update()`](#modulemap-update) 后不会发生变异。 + + +### Memory + ++ `Memory.scan(address, size, pattern, callbacks)`: 在由 `address` 和 `size` 给定的内存范围内扫描 `pattern` 的出现。 + {: #memory-scan} + + - `pattern` 必须是 "13 37 ?? ff" 的形式,以匹配 0x13 后跟 0x37 后跟任意字节后跟 0xff。 + 对于更高级的匹配,也可以指定 [r2][]-风格的掩码。掩码与 needle 和 haystack 进行按位与运算。 + 要指定掩码,请在 needle 后附加 `:` 字符,后跟使用相同语法的掩码。 + 例如:"13 37 13 37 : 1f ff ff f1"。 + 为了方便起见,也可以指定半字节级别的通配符,如 "?3 37 13 ?7",这在幕后被转换为掩码。 + + - `callbacks` 是一个对象,具有: + + - `onMatch(address, size)`: 使用包含出现地址的 `address`(作为 [`NativePointer`](#nativepointer)) + 和指定大小的 `size`(作为数字)调用。 + + 此函数可以返回字符串 `stop` 以提前取消内存扫描。 + + - `onError(reason)`: 当扫描时发生内存访问错误时使用 `reason` 调用 + + - `onComplete()`: 当内存范围已被完全扫描时调用 + +- `Memory.scanSync(address, size, pattern)`: [`scan()`](#memory-scan) 的同步版本, + 返回包含以下属性的对象数组: + + - `address`: [`NativePointer`](#nativepointer) 形式的绝对地址。 + - `size`: 字节大小 + + 例如: + +{% highlight js %} +// Find the module for the program itself, always at index 0: +const m = Process.enumerateModules()[0]; + +// Or load a module by name: +//const m = Module.load('win32u.dll'); + +// Print its properties: +console.log(JSON.stringify(m)); + +// Dump it from its base address: +console.log(hexdump(m.base)); + +// The pattern that you are interested in: +const pattern = '00 00 00 00 ?? 13 37 ?? 42'; + +Memory.scan(m.base, m.size, pattern, { + onMatch(address, size) { + console.log('Memory.scan() found match at', address, + 'with size', size); + + // Optionally stop scanning early: + return 'stop'; + }, + onComplete() { + console.log('Memory.scan() complete'); + } +}); + +const results = Memory.scanSync(m.base, m.size, pattern); +console.log('Memory.scanSync() result:\n' + + JSON.stringify(results)); +{% endhighlight %} + ++ `Memory.alloc(size[, options])`: 在堆上分配 `size` 字节的内存,或者,如果 `size` 是 + [`Process.pageSize`](#process-pagesize) 的倍数,则分配由操作系统管理的一个或多个原始内存页面。 + 当使用页面粒度时,如果你需要分配的内存靠近给定地址,你也可以指定一个 `options` 对象, + 通过指定 `{ near: address, maxDistance: distanceInBytes }`。 + 返回的值是一个 [`NativePointer`](#nativepointer),并且当所有对它的 JavaScript 句柄都消失时, + 底层内存将被释放。这意味着当指针被 JavaScript 运行时之外的代码使用时,你需要保留对它的引用。 + {: #memory-alloc} + ++ `Memory.copy(dst, src, n)`: 就像 memcpy()。不返回任何内容。 + {: #memory-copy} + + - dst: 指定目标基地址的 [`NativePointer`](#nativepointer)。 + - src: 指定源基地址的 [`NativePointer`](#nativepointer)。 + - n: 要复制的字节大小。 + ++ `Memory.dup(address, size)`: [`Memory.alloc()`](#memory-alloc) 后跟 [`Memory.copy()`](#memory-copy) 的简写。 + 返回包含新分配内存基地址的 [`NativePointer`](#nativepointer)。 + 有关内存分配的生命周期的详细信息,请参阅 [`Memory.copy()`](#memory-copy)。 + ++ `Memory.protect(address, size, protection)`: 更新内存区域的保护,其中 `protection` 是与 + [`Process.enumerateRanges()`](#process-enumerateranges) 格式相同的字符串。 + + 返回一个布尔值,指示操作是否成功完成。 + + 例如: + +{% highlight js %} +Memory.protect(ptr('0x1234'), 4096, 'rw-'); +{% endhighlight %} + ++ `Memory.queryProtection(address)`: 确定 `address` 处内存的当前保护,指定为 **[NativePointer](#nativepointer)**。 + 返回与 [`Process.enumerateRanges()`](#process-enumerateranges) 格式相同的页面保护字符串。 + ++ `Memory.patchCode(address, size, apply)`: 安全地修改 `address` 处的 `size` 字节, + 指定为 **[NativePointer](#nativepointer)**。提供的 JavaScript 函数 `apply` 会被调用, + 带有一个可写指针,你必须在返回之前在该指针处写入所需的修改。不要假设这与 `address` 是同一个位置, + 因为某些系统要求将修改写入临时位置,然后再映射到原始内存页面之上的内存中(例如在 iOS 上, + 直接修改内存中的代码可能会导致进程丢失其 CS_VALID 状态)。 + {: #memory-patchcode} + + 例如: + +{% highlight js %} +const gameEngine = Process.getModuleByName('game-engine.so'); +const getLivesLeft = gameEngine.getExportByName('get_lives_left'); +const maxPatchSize = 64; // Do not write out of bounds, may be a temporary buffer! +Memory.patchCode(getLivesLeft, maxPatchSize, code => { + const cw = new X86Writer(code, { pc: getLivesLeft }); + cw.putMovRegU32('eax', 9000); + cw.putRet(); + cw.flush(); +}); +{% endhighlight %} + ++ `Memory.allocUtf8String(str)`, + `Memory.allocUtf16String(str)`, + `Memory.allocAnsiString(str)`: + 在堆上分配、编码并写出 `str` 作为 UTF-8/UTF-16/ANSI 字符串。 + 返回的对象是一个 [`NativePointer`](#nativepointer)。 + 有关其生命周期的详细信息,请参阅 [`Memory.alloc()`](#memory-alloc)。 + + +### MemoryAccessMonitor + ++ `MemoryAccessMonitor.enable(ranges, callbacks)`: 监视一个或多个内存范围的访问, + 并在每次包含的内存页面首次访问时发出通知。`ranges` 是单个范围对象或此类对象的数组, + 每个对象包含: + {: #memoryaccessmonitor-enable} + + - `base`: [`NativePointer`](#nativepointer) 形式的基地址 + - `size`: 字节大小 + + `callbacks` 是一个对象,指定: + + - `onAccess(details)`: 使用 `details` 对象同步调用,包含: + - `threadId`: 执行访问的线程 ID,作为数字。 + - `operation`: 触发访问的操作类型,指定为 `read`、`write` 或 `execute` 的字符串 + - `from`: 执行访问的指令地址,作为 [`NativePointer`](#nativepointer) + - `address`: 正在访问的地址,作为 [`NativePointer`](#nativepointer) + - `rangeIndex`: 访问范围在提供给 `MemoryAccessMonitor.enable()` 的范围中的索引 + - `pageIndex`: 访问的内存页面在指定范围内的索引 + - `pagesCompleted`: 到目前为止已访问(且不再受监控)的页面总数 + - `pagesTotal`: 最初受监控的页面总数 + - `context`: CPU 寄存器,就像 [`Thread#context`](#thread-context)。 + 你也可以通过分配给这些键来更新寄存器值。 + ++ `MemoryAccessMonitor.disable()`: 停止监视传递给 [`MemoryAccessMonitor.enable()`](#memoryaccessmonitor-enable) + 的剩余内存范围。 + + +### CModule + ++ `new CModule(code[, symbols, options])`: 从提供的 `code` 创建一个新的 C 模块, + `code` 可以是包含要编译的 C 源代码的字符串,也可以是包含预编译共享库的 ArrayBuffer。 + C 模块被映射到内存中,并且可以完全被 JavaScript 访问。 + + 对于实现热回调非常有用,例如用于 **[Interceptor](#interceptor)** 和 **[Stalker](#stalker)**, + 但在需要启动新线程以紧密循环调用函数时也很有用,例如用于模糊测试目的。 + + 全局函数自动导出为 **[NativePointer](#nativepointer)** 属性,名称与 C 源代码中的完全相同。 + 这意味着你可以将它们传递给 **[Interceptor](#interceptor)** 和 **[Stalker](#stalker)**, + 或者使用 **[NativePointer](#nativepointer)** 调用它们。 + + 除了访问 Gum、GLib 和标准 C API 的精选子集外,映射进来的代码还可以通过暴露给它的 `symbols` 与 JavaScript 通信。 + 这是可选的第二个参数,一个指定附加符号名称及其 **[NativePointer](#nativepointer)** 值的对象, + 每个值都将在创建时插入。例如,这可以是使用 **[Memory.alloc()](#memory-alloc)** 分配的一个或多个内存块, + 和/或用于从 C 模块接收回调的 **[NativeCallback](#nativecallback)** 值。 + + 要执行初始化和清理,你可以定义具有以下名称和签名的函数: + + - `void init (void)` + - `void finalize (void)` + + 请注意,所有数据都是只读的,因此可写全局变量应声明为 *extern*,使用例如 **[Memory.alloc()](#memory-alloc)** 分配, + 并通过构造函数的第二个参数作为符号传入。 + + 可选的第三个参数 `options` 是一个对象,可用于指定要使用的工具链,例如:`{ toolchain: 'external' }`。 + 支持的值为: + + - `internal`: 使用 TinyCC,它静态链接到运行时中。从不接触文件系统,甚至在沙盒进程中也能工作。 + 但是生成的代码没有优化,因为 TinyCC 针对小编译器占用空间和短编译时间进行了优化。 + - `external`: 使用目标系统提供的工具链,假设我们在其中执行的进程可以访问它。 + - `any`: 如果 TinyCC 支持 [`Process.arch`](#process-arch),则与 `internal` 相同,否则为 `external`。 + 如果未指定,这是默认行为。 + +- `dispose()`: 立即从内存中取消映射模块。当不希望等待未来的垃圾回收时,这对于短寿命模块很有用。 + ++ `builtins`: 一个指定从 C 源代码构造 CModule 时存在的内置函数的对象。 + 这通常由诸如 `frida-create` 之类的脚手架工具使用,以便设置与 CModule 使用的相匹配的构建环境。 + 确切的内容取决于 [`Process.arch`](#process-arch) 和 Frida 版本,但可能看起来像下面这样: + + { + defines: { + 'GLIB_SIZEOF_VOID_P': '8', + 'G_GINT16_MODIFIER': '"h"', + 'G_GINT32_MODIFIER': '""', + 'G_GINT64_MODIFIER': '"ll"', + 'G_GSIZE_MODIFIER': '"l"', + 'G_GSSIZE_MODIFIER': '"l"', + 'HAVE_I386': true + }, + headers: { + 'gum/arch-x86/gumx86writer.h': '…', + 'gum/gumdefs.h': '…', + 'gum/guminterceptor.h': '…', + 'gum/gummemory.h': '…', + 'gum/gummetalarray.h': '…', + 'gum/gummetalhash.h': '…', + 'gum/gummodulemap.h': '…', + 'gum/gumprocess.h': '…', + 'gum/gumspinlock.h': '…', + 'gum/gumstalker.h': '…', + 'glib.h': '…', + 'json-glib/json-glib.h': '…', + 'capstone.h': '…' + } + } + +#### 示例 + +{% highlight js %} +const cm = new CModule(` +#include + +void hello(void) { + printf("Hello World from CModule\\n"); +} +`); + +console.log(JSON.stringify(cm)); + +const hello = new NativeFunction(cm.hello, 'void', []); +hello(); +{% endhighlight %} + +你可以使用 Frida 的 REPL 加载它: + +{% highlight sh %} +$ frida -p 0 -l example.js +{% endhighlight %} + +(REPL 监视磁盘上的文件并在更改时重新加载脚本。) + +然后你可以在 REPL 中输入 `hello()` 来调用 C 函数。 + +对于原型设计,我们建议使用 Frida REPL 的内置 CModule 支持: + +{% highlight sh %} +$ frida -p 0 -C example.c +{% endhighlight %} + +你也可以添加 `-l example.js` 来在其旁边加载一些 JavaScript。 +JavaScript 代码可以使用名为 `cm` 的全局变量来访问 CModule 对象, +但仅在调用 [`rpc.exports.init()`](#rpc-exports) 之后,因此请在那里执行任何依赖于 CModule 的初始化。 +你也可以通过分配给名为 `cs` 的全局对象来注入符号,但这必须在调用 [`rpc.exports.init()`](#rpc-exports) *之前*完成。 + +这是一个例子: + +![CModule REPL example](https://pbs.twimg.com/media/EEyxQzwXoAAqoAw?format=jpg&name=small) + +有关 CModule 的更多详细信息,请参阅 **[Frida 12.7 发行说明]({{ +site.baseurl_root }}/news/2019/09/18/frida-12-7-released/)**。 + + +### RustModule + +将 Rust 源代码编译为机器代码,直接存入内存。 + ++ `new RustModule(code[, symbols, options])`: 从提供的 `code` 创建一个新的 Rust 模块, + `code` 是包含要编译的 Rust 源代码的字符串。Rust 模块被映射到内存中,并且可以完全被 JavaScript 访问。 + + 对于实现热回调非常有用,例如用于 [Interceptor](#interceptor) 和 [Stalker](#stalker), + 但在需要启动新线程以紧密循环调用函数时也很有用,例如用于模糊测试目的。 + + 公共函数自动导出为 [`NativePointer`](#nativepointer) 属性。这意味着你可以将它们传递给 + Interceptor 和 Stalker,或者使用 [`NativeFunction`](#nativefunction) 调用它们。 + 在这种情况下,你通常希望确保它们被标记为 `#[no_mangle]` 和 `extern "C"`。 + + 除了 Rust 库之外,映射进来的代码还可以通过暴露给它的 `symbols` 与 JavaScript 通信。 + 此对象将符号名称映射到 [`NativePointer`](#nativepointer) 值。在你的 Rust 源代码中将它们声明为 `extern "C"`。 + 例如,这可以是使用 `Memory.alloc()` 分配的一个或多个内存块,和/或用于从 Rust 模块接收回调的 + [`NativeCallback`](#nativecallback) 值。 + + 可选的第三个参数 `options` 是一个对象,可用于指定要使用的 Cargo 依赖项,例如: + `{ dependencies: ['base64 = "0.22.1"', 'anyhow = "1.0.97"'] }`。 + + +### ApiResolver + ++ `new ApiResolver(type)`: 创建给定 `type` 的新解析器,允许你按名称快速查找 API,允许使用 glob。 + 确切可用的解析器取决于当前平台和当前进程中加载的运行时。截至撰写本文时,可用的解析器有: + + - `module`: 解析模块导出、导入和部分。始终可用。 + - `swift`: 解析 Swift 函数。 + 在加载了 Swift 运行时的进程中可用。使用 `Swift.available` 在运行时检查, + 或者将你的 `new ApiResolver('swift')` 调用包装在 *try-catch* 中。 + - `objc`: 解析 Objective-C 方法。 + 在 macOS 和 iOS 上,在加载了 Objective-C 运行时的进程中可用。 + 使用 [`ObjC.available`](#objc-available) 在运行时检查, + 或者将你的 `new ApiResolver('objc')` 调用包装在 *try-catch* 中。 + + 解析器将在创建时加载所需的最小数据量,并根据收到的查询延迟加载其余数据。 + 因此,建议对一批查询使用相同的实例,但为将来的批次重新创建它以避免查看过时的数据。 + +- `enumerateMatches(query)`: 执行解析器特定的 `query` 字符串,可选地后缀 `/i` 以执行不区分大小写的匹配, + 返回包含以下属性的对象数组: + + - `name`: 找到的 API 的名称 + - `address`: [`NativePointer`](#nativepointer) 形式的地址 + - `size`: 如果存在,指定字节大小的数字 + +{% highlight js %} +const resolver = new ApiResolver('module'); +const matches = resolver.enumerateMatches('exports:*!open*'); +const first = matches[0]; +/* + * Where `first` is an object similar to: + * + * { + * name: '/usr/lib/libSystem.B.dylib!opendir$INODE64', + * address: ptr('0x7fff870135c9') + * } + */ +{% endhighlight %} + +{% highlight js %} +const resolver = new ApiResolver('module'); +const matches = resolver.enumerateMatches('sections:*!*text*'); +const first = matches[0]; +/* + * Where `first` is an object similar to: + * + * { + * name: '/usr/lib/libSystem.B.dylib!0.__TEXT.__text', + * address: ptr('0x191c1e504'), + * size: 1528 + * } + */ +{% endhighlight %} + +{% highlight js %} +const resolver = new ApiResolver('swift'); +const matches = resolver.enumerateMatches('functions:*CoreDevice!*RemoteDevice*'); +const first = matches[0]; +/* + * Where `first` is an object similar to: + * + * { + * name: '/Library/Developer/PrivateFrameworks/CoreDevice.framework/Versions/A/CoreDevice!dispatch thunk of CoreDevice.RemoteDevice.addDeviceInfoChanged(on: __C.OS_dispatch_queue?, handler: (Foundation.UUID, CoreDeviceProtocols.DeviceInfo) -> ()) -> CoreDevice.Invalidatable', + * address: ptr('0x1078c3570') + * } + */ +{% endhighlight %} + +{% highlight js %} +const resolver = new ApiResolver('objc'); +const matches = resolver.enumerateMatches('-[NSURL* *HTTP*]'); +const first = matches[0]; +/* + * Where `first` is an object similar to: + * + * { + * name: '-[NSURLRequest valueForHTTPHeaderField:]', + * address: ptr('0x7fff94183e22') + * } + */ +{% endhighlight %} + + +### DebugSymbol + ++ `DebugSymbol.fromAddress(address)`, `DebugSymbol.fromName(name)`: + 查找 `address`/`name` 的调试信息并将其作为包含以下内容的对象返回: + + - `address`: 此符号的地址,作为 [`NativePointer`](#nativepointer)。 + - `name`: 符号名称,作为字符串,如果未知则为 null。 + - `moduleName`: 拥有此符号的模块名称,作为字符串,如果未知则为 null。 + - `fileName`: 拥有此符号的文件名,作为字符串,如果未知则为 null。 + - `lineNumber`: `fileName` 中的行号,作为数字,如果未知则为 null。 + + 你也可以对其调用 `toString()`,当与 [`Thread.backtrace()`](#thread-backtrace) 结合使用时非常有用: + +{% highlight js %} +const commonCrypto = Process.getModuleByName('libcommonCrypto.dylib'); +const f = commonCrypto.getExportByName('CCCryptorCreate'); +Interceptor.attach(f, { + onEnter(args) { + console.log('CCCryptorCreate called from:\n' + + Thread.backtrace(this.context, Backtracer.ACCURATE) + .map(DebugSymbol.fromAddress).join('\n') + '\n'); + } +}); +{% endhighlight %} + ++ `DebugSymbol.getFunctionByName(name)`: 解析函数名称并将其地址作为 [`NativePointer`](#nativepointer) 返回。 + 如果找到多个函数,则返回第一个。如果无法解析名称,则抛出异常。 + ++ `DebugSymbol.findFunctionsNamed(name)`: 解析函数名称并将其地址作为 [`NativePointer`](#nativepointer) 对象数组返回。 + ++ `DebugSymbol.findFunctionsMatching(glob)`: 解析匹配 `glob` 的函数名称并将其地址作为 + [`NativePointer`](#nativepointer) 对象数组返回。 + ++ `DebugSymbol.load(path)`: 加载特定模块的调试符号。 + + +### Kernel + ++ `Kernel.available`: 指定 Kernel API 是否可用的布尔值。除非如此,否则不要调用任何其他 `Kernel` 属性或方法。 + ++ `Kernel.base`: 内核的基地址,作为 **[UInt64](#uint64)**。 + ++ `Kernel.pageSize`: 内核页面大小(以字节为单位),作为数字。 + ++ `Kernel.enumerateModules()`: 枚举当前加载的内核模块,返回包含以下属性的对象数组: + + - `name`: 规范模块名称字符串 + - `base`: [`NativePointer`](#nativepointer) 形式的基地址 + - `size`: 字节大小 + ++ `Kernel.enumerateRanges(protection|specifier)`: 枚举满足给定 `protection` 字符串(形式为 `rwx`, + 其中 `rw-` 表示“必须至少可读且可写”)的内核内存范围。或者,你可以提供一个 `specifier` 对象, + 其中包含一个 `protection` 键(其值如前所述)和一个 `coalesce` 键(设置为 `true`, + 如果你希望合并具有相同保护的相邻范围(默认为 `false`;即保持范围分开))。 + 返回包含以下属性的对象数组: + {: #kernel-enumerateranges} + + - `base`: [`NativePointer`](#nativepointer) 形式的基地址 + - `size`: 字节大小 + - `protection`: 保护字符串(见上文) + ++ `Kernel.enumerateModuleRanges(name, protection)`: 就像 [`Kernel.enumerateRanges`](#kernel-enumerateranges), + 除了它的范围限定于指定的模块 `name`——对于内核本身的模块,它可以是 `null`。 + 每个范围还有一个 `name` 字段,包含作为字符串的唯一标识符。 + ++ `Kernel.alloc(size)`: 分配 `size` 字节的内核内存,向上取整为内核页面大小的倍数。 + 返回的值是指定分配基地址的 [`UInt64`](#uint64)。 + ++ `Kernel.protect(address, size, protection)`: 更新内核内存区域的保护,其中 `protection` 是与 + [`Kernel.enumerateRanges()`](#kernel-enumerateranges) 格式相同的字符串。 + + 例如: + +{% highlight js %} +Kernel.protect(UInt64('0x1234'), 4096, 'rw-'); +{% endhighlight %} + ++ `Kernel.readByteArray(address, length)`: 就像 + [`NativePointer#readByteArray`](#nativepointer-readbytearray),但从内核内存读取。 + ++ `Kernel.writeByteArray(address, bytes)`: 就像 + [`NativePointer#writeByteArray`](#nativepointer-writebytearray),但写入内核内存。 + ++ `Kernel.scan(address, size, pattern, callbacks)`: 就像 [`Memory.scan`](#memory-scan), + 但扫描内核内存。 + {: #kernel-scan} + +- `Kernel.scanSync(address, size, pattern)`: [`scan()`](#kernel-scan) 的同步版本, + 返回数组中的匹配项。 + +--- + +## 数据类型、函数和回调 + + +### Int64 + ++ `new Int64(v)`: 从 `v` 创建一个新的 Int64,`v` 可以是数字或包含十进制值的字符串, + 如果以 "0x" 为前缀,则为十六进制。为了简洁起见,你可以使用 `int64(v)` 简写。 + +- `add(rhs)`, `sub(rhs)`, + `and(rhs)`, `or(rhs)`, + `xor(rhs)`: + 创建一个新的 Int64,其值为此 Int64 加/减/与/或/异或 `rhs`,`rhs` 可以是数字或另一个 Int64 + +- `shr(n)`, `shl(n)`: + 创建一个新的 Int64,其值为此 Int64 右移/左移 `n` 位 + +- `compare(rhs)`: 返回整数比较结果,就像 + **[String#localeCompare()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare)** + +- `toNumber()`: 将此 Int64 转换为数字 + +- `toString([radix = 10])`: 转换为可选基数的字符串(默认为 10) + + +### UInt64 + ++ `new UInt64(v)`: 从 `v` 创建一个新的 UInt64,`v` 可以是数字或包含十进制值的字符串, + 如果以 "0x" 为前缀,则为十六进制。为了简洁起见,你可以使用 `uint64(v)` 简写。 + +- `add(rhs)`, `sub(rhs)`, + `and(rhs)`, `or(rhs)`, + `xor(rhs)`: + 创建一个新的 UInt64,其值为此 UInt64 加/减/与/或/异或 `rhs`,`rhs` 可以是数字或另一个 UInt64 + +- `shr(n)`, `shl(n)`: + 创建一个新的 UInt64,其值为此 UInt64 右移/左移 `n` 位 + +- `compare(rhs)`: 返回整数比较结果,就像 + **[String#localeCompare()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare)** + +- `toNumber()`: 将此 UInt64 转换为数字 + +- `toString([radix = 10])`: 转换为可选基数的字符串(默认为 10) + + +### NativePointer + ++ `new NativePointer(s)`: 从字符串 `s` 创建一个新的 **[NativePointer](#nativepointer)**, + 该字符串包含十进制内存地址,如果以 '0x' 为前缀,则为十六进制。 + 为了简洁起见,你可以使用 `ptr(s)` 简写。 + +- `isNull()`: 返回一个布尔值,允许你方便地检查指针是否为 NULL + +- `add(rhs)`, `sub(rhs)`, + `and(rhs)`, `or(rhs)`, + `xor(rhs)`: + 创建一个新的 **[NativePointer](#nativepointer)**,其值为此 **[NativePointer](#nativepointer)** + 加/减/与/或/异或 `rhs`,`rhs` 可以是数字或另一个 **[NativePointer](#nativepointer)** + +- `shr(n)`, `shl(n)`: + 创建一个新的 **[NativePointer](#nativepointer)**,其值为此 **[NativePointer](#nativepointer)** + 右移/左移 `n` 位 + +- `not()`: 创建一个新的 **[NativePointer](#nativepointer)**,其值为此 **[NativePointer](#nativepointer)** 的位取反 + +- `sign([key, data])`: 通过获取此 **[NativePointer](#nativepointer)** 的位并添加指针身份验证位来创建新的 + **[NativePointer](#nativepointer)**,从而创建签名指针。如果当前进程不支持指针身份验证,则这是一个无操作, + 返回此 **[NativePointer](#nativepointer)** 而不是新值。 + {: #nativepointer-sign} + + 可选地,可以将 `key` 指定为字符串。支持的值为: + - ia: IA 密钥,用于签署代码指针。这是默认值。 + - ib: IB 密钥,用于签署代码指针。 + - da: DA 密钥,用于签署数据指针。 + - db: DB 密钥,用于签署数据指针。 + + `data` 参数也可以指定为 **[NativePointer](#nativepointer)**/类数字值, + 以提供用于签名的额外数据,默认为 `0`。 + +- `strip([key])`: 通过获取此 **[NativePointer](#nativepointer)** 的位并移除其指针身份验证位来创建新的 + **[NativePointer](#nativepointer)**,从而创建原始指针。 + 如果当前进程不支持指针身份验证,则这是一个无操作,返回此 **[NativePointer](#nativepointer)** 而不是新值。 + + 可选地,可以传递 `key` 以指定用于签署被剥离指针的密钥。默认为 `ia`。 + (有关支持的值,请参阅 [`sign()`](#nativepointer-sign)。) + +- `blend(smallInteger)`: 通过获取此 **[NativePointer](#nativepointer)** 的位并将它们与常量混合来创建新的 + **[NativePointer](#nativepointer)**,该常量又可以作为 `data` 传递给 [`sign()`](#nativepointer-sign)。 + +- `equals(rhs)`: 返回一个布尔值,指示 `rhs` 是否等于此指针;即它具有相同的指针值 + +- `compare(rhs)`: 返回整数比较结果,就像 + **[String#localeCompare()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare)** + +- `toInt32()`: 将此 **[NativePointer](#nativepointer)** 转换为有符号 32 位整数 + +- `toString([radix = 16])`: 转换为可选基数的字符串(默认为 16) + +- `toMatchPattern()`: 返回一个包含此指针原始值的 [`Memory.scan()`](#memory-scan) 兼容匹配模式的字符串 + +- `readPointer()`: 从此内存位置读取 [`NativePointer`](#nativepointer)。 + + 如果地址不可读,将抛出 JavaScript 异常。 + +- `writePointer(ptr)`: 将 `ptr` 写入此内存位置。 + + 如果地址不可写,将抛出 JavaScript 异常。 + +- `readS8()`, `readU8()`, + `readS16()`, `readU16()`, + `readS32()`, `readU32()`, + `readShort()`, `readUShort()`, + `readInt()`, `readUInt()`, + `readFloat()`, `readDouble()`: + 从此内存位置读取有符号或无符号 8/16/32/等或浮点/双精度值,并将其作为数字返回。 + + 如果地址不可读,将抛出 JavaScript 异常。 + +- `writeS8(value)`, `writeU8(value)`, + `writeS16(value)`, `writeU16(value)`, + `writeS32(value)`, `writeU32(value)`, + `writeShort(value)`, `writeUShort(value)`, + `writeInt(value)`, `writeUInt(value)`, + `writeFloat(value)`, `writeDouble(value)`: + 将有符号或无符号 8/16/32/等或浮点/双精度 `value` 写入此内存位置。 + + 如果地址不可写,将抛出 JavaScript 异常。 + +- `readS64()`, `readU64()`, + `readLong()`, `readULong()`: + 从此内存位置读取有符号或无符号 64 位或长整型值,并将其作为 **[Int64](#int64)**/**[UInt64](#uint64)** 值返回。 + + 如果地址不可读,将抛出 JavaScript 异常。 + +- `writeS64(value)`, `writeU64(value)`, + `writeLong(value)`, `writeULong(value)`: + 将 **[Int64](#int64)**/**[UInt64](#uint64)** `value` 写入此内存位置。 + + 如果地址不可写,将抛出 JavaScript 异常。 + +- `readByteArray(length)`: 从此内存位置读取 `length` 字节,并将其作为 **[ArrayBuffer](#arraybuffer)** 返回。 + 通过将其作为第二个参数传递给 [`send()`](#communication-send),可以将此缓冲区有效地传输到基于 Frida 的应用程序。 + {: #nativepointer-readbytearray} + + 如果从地址读取的 `length` 字节中的任何一个不可读,将抛出 JavaScript 异常。 + +- `writeByteArray(bytes)`: 将 `bytes` 写入此内存位置,其中 `bytes` 要么是 **[ArrayBuffer](#arraybuffer)** + (通常从 `readByteArray()` 返回),要么是 0 到 255 之间的整数数组。例如:`[ 0x13, 0x37, 0x42 ]`。 + {: #nativepointer-writebytearray} + + 如果写入地址的任何字节不可写,将抛出 JavaScript 异常。 + +- `readVolatile(length)`, `writeVolatile(bytes)`: 就像 + [`NativePointer#readByteArray`](#nativepointer-readbytearray) 和 + [`NativePointer#writeByteArray`](#nativepointer-writebytearray),但在内存不可访问的情况下避免生成本机异常。 + 这意味着内存访问速度较慢,因为涉及一个或多个系统调用,但在我们的本机异常处理失败或不可用且错误访问会导致进程崩溃的情况下可以安全使用。 + 如果你在应用程序线程可能正在运行时转储内存,或者指针引用的内存可能不再可访问,请使用此 API。 + +- `readCString([size = -1])`, + `readUtf8String([size = -1])`, + `readUtf16String([length = -1])`, + `readAnsiString([size = -1])`: + 将此内存位置的字节读取为 ASCII、UTF-8、UTF-16 或 ANSI 字符串。 + 如果你知道字符串的字节大小,请提供可选的 `size` 参数,或者如果字符串以 NUL 结尾,则省略它或指定 *-1*。 + 同样,如果你知道字符串的字符长度,你可以提供可选的 `length` 参数。 + + 如果从地址读取的 `size` / `length` 字节中的任何一个不可读,将抛出 JavaScript 异常。 + + 请注意,`readAnsiString()` 仅在 Windows 上可用(且相关)。 + +- `writeUtf8String(str)`, + `writeUtf16String(str)`, + `writeAnsiString(str)`: + 将 JavaScript 字符串编码并写入此内存位置(带有 NUL 终止符)。 + + 如果写入地址的任何字节不可写,将抛出 JavaScript 异常。 + + 请注意,`writeAnsiString()` 仅在 Windows 上可用(且相关)。 + + +### ArrayBuffer + ++ `wrap(address, size)`: 创建一个由现有内存区域支持的 ArrayBuffer,其中 `address` 是指定区域基地址的 + [`NativePointer`](#nativepointer),`size` 是指定其大小的数字。 + 与 [`NativePointer`](#nativepointer) 读/写 API 不同,访问时不执行验证,这意味着错误的指针会导致进程崩溃。 + +- `unwrap()`: 返回指定 ArrayBuffer 后备存储基地址的 [`NativePointer`](#nativepointer)。 + 调用者有责任在后备存储仍在使用时保持缓冲区处于活动状态。 + + +### NativeFunction + ++ `new NativeFunction(address, returnType, argTypes[, abi])`: 创建一个新的 NativeFunction 来调用 `address` + (用 [`NativePointer`](#nativepointer) 指定)处的函数,其中 `returnType` 指定返回类型, + `argTypes` 数组指定参数类型。如果不是系统默认值,你还可以选择指定 `abi`。 + 对于可变参数函数,在固定参数和可变参数之间向 `argTypes` 添加一个 `'...'` 条目。 + + - #### 按值传递的结构体和类 + + 对于按值传递的结构体或类,提供一个包含结构体字段类型的数组,而不是字符串。 + 你可以根据需要嵌套这些数组以表示结构体内的结构体。 + 请注意,返回的对象也是一个 [`NativePointer`](#nativepointer),因此可以传递给 [`Interceptor#attach`](#interceptor-attach)。 + + 这必须与结构体/类完全匹配,因此如果你有一个包含三个 int 的结构体,你必须传递 `['int', 'int', 'int']`。 + + 对于具有虚方法的类,第一个字段将是指向 **[vtable](https://en.wikipedia.org/wiki/Virtual_method_table)** 的指针。 + + 对于涉及返回值大于 [`Process.pointerSize`](#process-pointersize) 的 C++ 场景, + 典型的 ABI 可能期望必须将指向预分配空间的 [`NativePointer`](#nativepointer) 作为第一个参数传入。 + (例如,这种情况在 WebKit 中很常见。) + + - #### 支持的类型 + - void + - pointer + - int + - uint + - long + - ulong + - char + - uchar + - size_t + - ssize_t + - float + - double + - int8 + - uint8 + - int16 + - uint16 + - int32 + - uint32 + - int64 + - uint64 + - bool + + - #### 支持的 ABI + - default + - Windows 32-bit: + - sysv + - stdcall + - thiscall + - fastcall + - mscdecl + - Windows 64-bit: + - win64 + - UNIX x86: + - sysv + - unix64 + - UNIX ARM: + - sysv + - vfp + ++ `new NativeFunction(address, returnType, argTypes[, options])`: 就像前一个构造函数, + 但第四个参数 `options` 是一个对象,可能包含以下一个或多个键: + + - `abi`: 与上面的枚举相同。 + - `scheduling`: 调度行为作为字符串。支持的值为: + - cooperative: 允许其他线程在调用本机函数时执行 JavaScript 代码,即在调用之前释放锁,并在之后重新获取它。 + 这是默认行为。 + - exclusive: 不允许其他线程在调用本机函数时执行 JavaScript 代码,即保持持有 JavaScript 锁。 + 这更快,但可能导致死锁。 + - `exceptions`: 异常行为作为字符串。支持的值为: + - steal: 如果调用的函数生成本机异常,例如通过解引用无效指针,Frida 将展开堆栈并窃取异常, + 将其转换为可以处理的 JavaScript 异常。这可能会使应用程序处于未定义状态, + 但在实验时避免进程崩溃很有用。 + 这是默认行为。 + - propagate: 让应用程序处理函数调用期间发生的任何本机异常。(或者,通过 + [`Process.setExceptionHandler()`](#process-setexceptionhandler) 安装的处理程序。) + - `traps`: 要启用的代码陷阱,作为字符串。支持的值为: + - default: 如果函数调用触发了任何钩子,则将调用 **[Interceptor.attach()](#interceptor-attach)** 回调。 + - none: 防止 **[Interceptor](#interceptor)** 和 **[Stalker](#stalker)** 触发。 + - all: 除了 **[Interceptor](#interceptor)** 回调之外,**[Stalker](#stalker)** + 也可能在每个函数调用期间暂时重新激活。这对于例如在引导模糊器时测量代码覆盖率、 + 在调试器中实现“步入”等非常有用。 + 请注意,在使用 **[Java](#java)** 和 **[ObjC](#objc)** API 时这也是可能的, + 因为方法包装器也提供了一个 `clone(options)` API 来创建具有自定义 NativeFunction 选项的新方法包装器。 + + +### NativeCallback + ++ `new NativeCallback(func, returnType, argTypes[, abi])`: 创建一个由 JavaScript 函数 `func` 实现的新 NativeCallback, + 其中 `returnType` 指定返回类型,`argTypes` 数组指定参数类型。如果不是系统默认值,你也可以指定 abi。 + 有关支持的类型和 abi 的详细信息,请参阅 [`NativeFunction`](#nativefunction)。 + 请注意,返回的对象也是一个 [`NativePointer`](#nativepointer),因此可以传递给 [`Interceptor#replace`](#interceptor-replace)。 + 当将结果回调与 **[Interceptor.replace()](#interceptor-replace)** 一起使用时, + `func` 将被调用,`this` 绑定到一个具有一些有用属性的对象,就像 **[Interceptor.attach()](#interceptor-attach)** 中的那个一样。 + + +### SystemFunction + ++ `new SystemFunction(address, returnType, argTypes[, abi])`: 就像 [`NativeFunction`](#nativefunction), + 但也提供线程最后错误状态的快照。返回值是一个将实际返回值包装为 `value` 的对象, + 带有一个名为 `errno` (UNIX) 或 `lastError` (Windows) 的附加平台特定字段。 + ++ `new SystemFunction(address, returnType, argTypes[, options])`: 与上面相同, + 但接受像 [`NativeFunction`](#nativefunction) 的相应构造函数一样的 `options` 对象。 + +--- + +## 网络 + + +### Socket + ++ `Socket.listen([options])`: 打开一个 TCP 或 UNIX 监听套接字。返回一个接收 **[SocketListener](#socketlistener)** 的 *Promise*。 + + 默认情况下,如果支持,则监听 IPv4 和 IPv6,并在随机选择的 TCP 端口上绑定所有接口。 + + 可选的 `options` 参数是一个对象,可能包含以下一些键: + + - `family`: 地址族作为字符串。支持的值为: + - unix + - ipv4 + - ipv6 + 如果支持,默认为监听 `ipv4` 和 `ipv6`。 + - `host`: (IP family) IP 地址作为字符串。默认为所有接口。 + - `port`: (IP family) IP 端口作为数字。默认为任何可用端口。 + - `type`: (UNIX family) UNIX 套接字类型作为字符串。支持的类型为: + - anonymous + - path + - abstract + - abstract-padded + 默认为 `path`。 + - `path`: (UNIX family) UNIX 套接字路径作为字符串。 + - `backlog`: 监听积压作为数字。默认为 `10`。 + ++ `Socket.connect(options)`: 连接到 TCP 或 UNIX 服务器。返回一个接收 **[SocketConnection](#socketconnection)** 的 *Promise*。 + + `options` 参数是一个对象,应包含以下一些键: + + - `family`: 地址族作为字符串。支持的值为: + - unix + - ipv4 + - ipv6 + 默认为取决于指定的 `host` 的 IP 族。 + - `host`: (IP family) IP 地址作为字符串。默认为 `localhost`。 + - `port`: (IP family) IP 端口作为数字。 + - `type`: (UNIX family) UNIX 套接字类型作为字符串。支持的类型为: + - anonymous + - path + - abstract + - abstract-padded + 默认为 `path`。 + - `path`: (UNIX family) UNIX 套接字路径作为字符串。 + ++ `Socket.type(handle)`: 检查 OS 套接字 `handle` 并将其类型作为字符串返回, + 该字符串为 `tcp`、`udp`、`tcp6`、`udp6`、`unix:stream`、`unix:dgram`, + 如果无效或未知,则为 `null`。 + ++ `Socket.localAddress(handle)`, + `Socket.peerAddress(handle)`: + 检查 OS 套接字 `handle` 并返回其本地或对等地址,如果无效或未知,则返回 `null`。 + + 返回的对象具有以下字段: + + - `ip`: (IP sockets) IP 地址作为字符串。 + - `port`: (IP sockets) IP 端口作为数字。 + - `path`: (UNIX sockets) UNIX 路径作为字符串。 + + +### SocketListener + +所有方法都是完全异步的并返回 Promise 对象。

+ +- `path`: (UNIX family) 正在监听的路径。 + +- `port`: (IP family) 正在监听的 IP 端口。 + +- `close()`: 关闭监听器,释放与其相关的资源。一旦监听器关闭,所有其他操作都将失败。 + 允许多次关闭监听器,并且不会导致错误。 + +- `accept()`: 等待下一个客户端连接。返回的 *Promise* 接收一个 **[SocketConnection](#socketconnection)**。 + + +### SocketConnection + +继承自 **[IOStream](#iostream)**。 +所有方法都是完全异步的并返回 Promise 对象。

+ +- `setNoDelay(noDelay)`: 如果 `noDelay` 为 `true`,则禁用 Nagle 算法,否则启用它。 + Nagle 算法默认启用,因此仅当你希望优化低延迟而不是高吞吐量时才需要调用此方法。 + +--- + +## 文件和流 + + +### File + ++ `File.readAllBytes(path)`: 同步读取 `path` 指定的文件中的所有字节,并将其作为 `ArrayBuffer` 返回。 + ++ `File.readAllText(path)`: 同步读取 `path` 指定的文件中的所有文本,并将其作为字符串返回。 + 文件必须是 UTF-8 编码的,如果不是这种情况,将抛出异常。 + ++ `File.writeAllBytes(path, data)`: 同步将 `data` 写入 `path` 指定的文件,其中 `data` 是一个 `ArrayBuffer`。 + ++ `File.writeAllText(path, text)`: 同步将 `text` 写入 `path` 指定的文件,其中 `text` 是一个字符串。 + 文件将是 UTF-8 编码的。 + ++ `new File(filePath, mode)`: 打开或创建 `filePath` 处的文件,`mode` 字符串指定应如何打开它。 + 例如 `"wb"` 以二进制模式打开文件进行写入(这与 C 标准库中的 `fopen()` 格式相同)。 + +- `tell()`: 返回文件指针在文件中的当前位置。 + +- `seek(offset[, whence])`: 将文件指针移动到新位置。`offset` 是要移动到的位置, + `whence` 是偏移量的起始点(`File.SEEK_SET` 表示文件开头,`File.SEEK_CUR` 表示当前文件位置, + 或 `File.SEEK_END` 表示文件末尾)。 + +- `readBytes([size])`: 从当前文件指针位置开始从文件中读取并返回 `size` 字节作为 `ArrayBuffer`。 + 如果未指定 `size`,则从当前位置读取直到文件末尾。 + +- `readText([size])`: 从当前文件指针位置开始从文件中读取并返回 `size` 个字符作为字符串。 + 如果未指定 `size`,则从当前位置读取文本直到文件末尾。 + 正在读取的字节必须是 UTF-8 编码的,如果不是这种情况,将抛出异常。 + +- `readLine()`: 读取并返回下一行作为字符串。从当前文件指针位置开始读取。返回的行不包括换行符。 + +- `write(data)`: 同步将 `data` 写入文件,其中 `data` 是字符串或由 + [`NativePointer#readByteArray`](#nativepointer-readbytearray) 返回的缓冲区。 + +- `flush()`: 将任何缓冲数据刷新到底层文件。 + +- `close()`: 关闭文件。你应该在完成文件操作后调用此函数,除非你不介意在对象被垃圾回收或脚本卸载时发生这种情况。 + + +### IOStream + +所有方法都是完全异步的并返回 Promise 对象。

+ +- `input`: 要从中读取的 **[InputStream](#inputstream)**。 + +- `output`: 要写入的 **[OutputStream](#outputstream)**。 + +- `close()`: 关闭流,释放与其相关的资源。这也将关闭单独的输入和输出流。 + 一旦流关闭,所有其他操作都将失败。允许多次关闭流,并且不会导致错误。 + + +### InputStream + +所有方法都是完全异步的并返回 Promise 对象。

+ +- `close()`: 关闭流,释放与其相关的资源。一旦流关闭,所有其他操作都将失败。 + 允许多次关闭流,并且不会导致错误。 + +- `read(size)`: 从流中读取最多 `size` 字节。返回的 **Promise** 接收一个最长为 `size` 字节的 + **[ArrayBuffer](#arraybuffer)**。流的结束通过空缓冲区发出信号。 + +- `readAll(size)`: 继续从流中读取,直到正好消耗了 `size` 字节。返回的 *Promise* 接收一个正好 + `size` 字节长的 **[ArrayBuffer](#arraybuffer)**。过早错误或流结束会导致 *Promise* 被拒绝并显示错误, + 其中 `Error` 对象具有包含不完整数据的 `partialData` 属性。 + + +### OutputStream + +所有方法都是完全异步的并返回 Promise 对象。

+ +- `close()`: 关闭流,释放与其相关的资源。一旦流关闭,所有其他操作都将失败。 + 允许多次关闭流,并且不会导致错误。 + +- `write(data)`: 尝试将 `data` 写入流。`data` 值要么是 **[ArrayBuffer](#arraybuffer)**, + 要么是 0 到 255 之间的整数数组。返回的 *Promise* 接收一个 *Number*,指定有多少字节的 `data` 已写入流。 + +- `writeAll(data)`: 继续写入流,直到所有 `data` 都已写入。`data` 值要么是 **[ArrayBuffer](#arraybuffer)**, + 要么是 0 到 255 之间的整数数组。过早错误或流结束会导致错误,其中 `Error` 对象具有 `partialSize` 属性, + 指定在发生错误之前有多少字节的 `data` 已写入流。 + +- `writeMemoryRegion(address, size)`: 尝试将 `size` 字节写入流,从 `address` 读取它们, + `address` 是一个 [`NativePointer`](#nativepointer)。返回的 *Promise* 接收一个 *Number*, + 指定有多少字节的 `data` 已写入流。 + + +### UnixInputStream + +(仅在类 UNIX 操作系统上可用。)

+ ++ `new UnixInputStream(fd[, options])`: 从指定的文件描述符 `fd` 创建一个新的 **[InputStream](#inputstream)**。 + + 你也可以提供一个 `options` 对象,其中 `autoClose` 设置为 `true`,以使流在释放时(通过 `close()` 或未来的垃圾回收) + 关闭底层文件描述符。 + + +### UnixOutputStream + +(仅在类 UNIX 操作系统上可用。)

+ ++ `new UnixOutputStream(fd[, options])`: 从指定的文件描述符 `fd` 创建一个新的 **[OutputStream](#outputstream)**。 + + 你也可以提供一个 `options` 对象,其中 `autoClose` 设置为 `true`,以使流在释放时(通过 `close()` 或未来的垃圾回收) + 关闭底层文件描述符。 + + +### Win32InputStream + +(仅在 Windows 上可用。)

+ ++ `new Win32InputStream(handle[, options])`: 从指定的 `handle` 创建一个新的 **[InputStream](#inputstream)**, + `handle` 是一个 Windows *HANDLE* 值。 + + 你也可以提供一个 `options` 对象,其中 `autoClose` 设置为 `true`,以使流在释放时(通过 `close()` 或未来的垃圾回收) + 关闭底层句柄。 + + +### Win32OutputStream + +(仅在 Windows 上可用。)

+ ++ `new Win32OutputStream(handle[, options])`: 从指定的 `handle` 创建一个新的 **[OutputStream](#outputstream)**, + `handle` 是一个 Windows *HANDLE* 值。 + + 你也可以提供一个 `options` 对象,其中 `autoClose` 设置为 `true`,以使流在释放时(通过 `close()` 或未来的垃圾回收) + 关闭底层句柄。 + + +## 数据库 + + +### SqliteDatabase + ++ `SqliteDatabase.open(path[, options])`: 打开由 `path` 指定的 SQLite v3 数据库, + `path` 是包含数据库文件系统路径的字符串。默认情况下,数据库将以读写方式打开, + 但你可以通过提供一个 `options` 对象来自定义此行为,该对象具有名为 `flags` 的属性, + 指定包含以下一个或多个值的字符串数组:`readonly`、`readwrite`、`create`。 + 返回的 SqliteDatabase 对象将允许你对数据库执行查询。 + ++ `SqliteDatabase.openInline(encodedContents)`: 就像 `open()`,但数据库的内容作为包含其数据的字符串提供, + Base64 编码。我们建议在 Base64 编码之前对数据库进行 gzip 压缩,但这是可选的,通过查找 gzip 魔术标记来检测。 + 数据库以读写方式打开,但是 100% 在内存中,从不接触文件系统。这对于需要捆绑预计算数据缓存的代理非常有用, + 例如用于指导动态分析的静态分析数据。 + {: #sqlitedatabase-openinline} + +- `close()`: 关闭数据库。你应该在完成数据库操作后调用此函数,除非你不介意在对象被垃圾回收或脚本卸载时发生这种情况。 + +- `exec(sql)`: 执行原始 SQL 查询,其中 `sql` 是包含查询文本表示的字符串。 + 查询的结果被忽略,因此这应该仅用于设置数据库的查询,例如表创建。 + +- `prepare(sql)`: 将提供的 SQL 编译为 **[SqliteStatement](#sqlitestatement)** 对象, + 其中 `sql` 是包含查询文本表示的字符串。 + + 例如: + +{% highlight js %} +const db = SqliteDatabase.open('/path/to/people.db'); + +const smt = db.prepare('SELECT name, bio FROM people WHERE age = ?'); + +console.log('People whose age is 42:'); +smt.bindInteger(1, 42); +let row; +while ((row = smt.step()) !== null) { + const [name, bio] = row; + console.log('Name:', name); + console.log('Bio:', bio); +} +smt.reset(); +{% endhighlight %} + +- `dump()`: 将数据库转储为 Base64 编码的 gzip 压缩 blob,结果作为字符串返回。 + 这对于在代理代码中内联缓存非常有用,通过调用 [`SqliteDatabase.openInline()`](#sqlitedatabase-openinline) 加载。 + + +### SqliteStatement + +- `bindInteger(index, value)`: 将整数 `value` 绑定到 `index` +- `bindFloat(index, value)`: 将浮点数 `value` 绑定到 `index` +- `bindText(index, value)`: 将文本 `value` 绑定到 `index` +- `bindBlob(index, bytes)`: 将 blob `bytes` 绑定到 `index`,其中 `bytes` 是 + **[ArrayBuffer](#arraybuffer)**、字节值数组或字符串 +- `bindNull(index)`: 将空值绑定到 `index` +- `step()`: 要么开始新查询并获取第一个结果,要么移动到下一个结果。 + 返回一个包含按查询指定顺序排列的值的数组,或者当达到最后一个结果时返回 `null`。 + 如果你打算再次使用此对象,则应在那时调用 `reset()`。 +- `reset()`: 重置内部状态以允许后续查询 + +--- + +## 插桩 + + +### Interceptor + ++ `Interceptor.attach(target, callbacks[, data])`: 拦截对 `target` 处函数的调用。 + 这是一个 [`NativePointer`](#nativepointer),指定你想要拦截调用的函数的地址。 + 请注意,在 32 位 ARM 上,对于 ARM 函数,此地址的最低有效位必须设置为 0,对于 Thumb 函数,必须设置为 1。 + 如果你从 Frida API(例如 [`Module#getExportByName()`](#module-getexportbyname))获取地址,Frida 会为你处理此细节。 + {: #interceptor-attach} + + `callbacks` 参数是一个包含以下一个或多个属性的对象: + + - `onEnter(args)`: 给定一个参数 `args` 的回调函数,该参数可用于读取或写入参数, + 作为 [`NativePointer`](#nativepointer) 对象数组。 {: #interceptor-onenter} + + - `onLeave(retval)`: 给定一个参数 `retval` 的回调函数,该参数是包含原始返回值的 + [`NativePointer`](#nativepointer) 派生对象。 + 你可以调用 `retval.replace(1337)` 将返回值替换为整数 `1337`, + 或者调用 `retval.replace(ptr("0x1234"))` 将其替换为指针。 + 请注意,此对象在 *onLeave* 调用之间被回收,因此不要在回调之外存储和使用它。 + 如果你需要存储包含的值,请进行深层复制,例如:`ptr(retval.toString())`。 + {: #interceptor-onleave} + + 如果被钩住的函数非常热,`onEnter` 和 `onLeave` 可以是指向使用 **[CModule](#cmodule)** 编译的 + 本机 C 函数的 [`NativePointer`](#nativepointer) 值。它们的签名是: + + - `void onEnter (GumInvocationContext * ic)` + + - `void onLeave (GumInvocationContext * ic)` + + 在这种情况下,第三个可选参数 `data` 可以是一个 [`NativePointer`](#nativepointer), + 可以通过 `gum_invocation_context_get_listener_function_data()` 访问。 + + 你也可以通过传递一个函数而不是 `callbacks` 对象来拦截任意指令。此函数具有与 `onEnter` 相同的签名, + 但传递给它的 `args` 参数只有在被拦截的指令位于函数开头或寄存器/堆栈尚未偏离该点的位置时才会给你合理的值。 + + 就像上面一样,此函数也可以通过指定 [`NativePointer`](#nativepointer) 而不是函数来用 C 实现。 + + 返回一个你可以调用 `detach()` 的监听器对象。 + + 请注意,这些函数在调用时,`this` 会绑定到一个每次调用(线程本地)的对象,你可以在其中存储任意数据, + 如果你想在 `onEnter` 中读取参数并在 `onLeave` 中对其采取行动,这很有用。 + + 例如: + +{% highlight js %} +const libc = Process.getModuleByName('libc.so'); +Interceptor.attach(libc.getExportByName('read'), { + onEnter(args) { + this.fileDescriptor = args[0].toInt32(); + }, + onLeave(retval) { + if (retval.toInt32() > 0) { + /* do something with this.fileDescriptor */ + } + } +}); +{% endhighlight %} + ++ 此外,该对象包含一些有用的属性: + + - `returnAddress`: 返回地址作为 **[NativePointer](#nativepointer)** + + - `context`: 具有键 `pc` 和 `sp` 的对象,它们是指定 EIP/RIP/PC 和 ESP/RSP/SP 的 + **[NativePointer](#nativepointer)** 对象,分别用于 ia32/x64/arm。 + 其他特定于处理器的键也可用,例如 `eax`, `rax`, `r0`, `x0` 等。 + 你也可以通过分配给这些键来更新寄存器值。 + + - `errno`: (UNIX) 当前 errno 值(你可以替换它) + + - `lastError`: (Windows) 当前操作系统错误值(你可以替换它) + + - `threadId`: 操作系统线程 ID + + - `depth`: 相对于其他调用的调用深度 + + 例如: + +{% highlight js %} +Interceptor.attach(Module.getGlobalExportByName('read'), { + onEnter(args) { + console.log('Context information:'); + console.log('Context : ' + JSON.stringify(this.context)); + console.log('Return : ' + this.returnAddress); + console.log('ThreadId : ' + this.threadId); + console.log('Depth : ' + this.depth); + console.log('Errornr : ' + this.err); + + // Save arguments for processing in onLeave. + this.fd = args[0].toInt32(); + this.buf = args[1]; + this.count = args[2].toInt32(); + }, + onLeave(result) { + console.log('----------') + // Show argument 1 (buf), saved during onEnter. + const numBytes = result.toInt32(); + if (numBytes > 0) { + console.log(hexdump(this.buf, { length: numBytes, ansi: true })); + } + console.log('Result : ' + numBytes); + } +}) +{% endhighlight %} + +
+
性能注意事项
+

+ 提供的回调对性能有重大影响。如果你只需要检查参数但不关心返回值,或者反过来, + 请确保省略你不需要的回调;即避免将你的逻辑放在 onEnter 中并将 onLeave 留在那儿作为一个空回调。 +

+

+ 在 iPhone 5S 上,仅提供 onEnter 时的基本开销可能约为 6 微秒, + 而同时提供 onEnteronLeave 时约为 11 微秒。 +

+

+ 还要注意拦截对每秒调用无数次的函数的调用;虽然 **[send()](#communication-send)** 是异步的, + 但发送单个消息的总开销并未针对高频进行优化,因此这意味着 Frida 让你根据需要低延迟还是高吞吐量, + 自行决定将多个值批处理到单个 **[send()](#communication-send)** 调用中。 +

+

+ 但是,当钩住热函数时,你可以将 Interceptor 与 **[CModule](#cmodule)** 结合使用以在 C 中实现回调。 +

+
+ ++ `Interceptor.detachAll()`: 分离所有先前附加的回调。 + ++ `Interceptor.replace(target, replacement[, data])`: 用 `replacement` 处的实现替换 `target` 处的函数。 + 如果你想完全或部分替换现有函数的实现,通常使用此方法。 + {: #interceptor-replace} + + 使用 [`NativeCallback`](#nativecallback) 在 JavaScript 中实现 `replacement`。 + + 如果被替换的函数非常热,你可以使用 **[CModule](#cmodule)** 在 C 中实现 `replacement`。 + 然后你也可以指定第三个可选参数 `data`,这是一个可以通过 `gum_invocation_context_get_listener_function_data()` + 访问的 [`NativePointer`](#nativepointer)。使用 `gum_interceptor_get_current_invocation()` 获取 `GumInvocationContext *`。 + + 请注意,`replacement` 将保持活动状态,直到调用 [`Interceptor#revert`](#interceptor-revert)。 + + 如果你想链接到原始实现,你可以在你的实现中通过 [`NativeFunction`](#nativefunction) 同步调用 `target`, + 这将绕过并直接转到原始实现。 + + 这是一个例子: + +{% highlight js %} +const libc = Process.getModuleByName('libc.so'); +const openPtr = libc.getExportByName('open'); +const open = new NativeFunction(openPtr, 'int', ['pointer', 'int']); +Interceptor.replace(openPtr, new NativeCallback((pathPtr, flags) => { + const path = pathPtr.readUtf8String(); + log('Opening "' + path + '"'); + const fd = open(pathPtr, flags); + log('Got fd: ' + fd); + return fd; +}, 'int', ['pointer', 'int'])); +{% endhighlight %} + ++ `Interceptor.replaceFast(target, replacement)`: 就像 [`replace()`](#interceptor-replace), + 除了 `target` 被修改为直接跳转到你的替换,这意味着与 replace() 相比开销更小。 + 这也意味着如果你想调用原始实现,你需要使用返回的指针。 + ++ `Interceptor.revert(target)`: 将 `target` 处的函数恢复为先前的实现。 + {: #interceptor-revert} + ++ `Interceptor.flush()`: 确保任何挂起的更改已提交到内存。这应该仅在极少数必要的情况下完成, + 例如,如果你刚刚 **[attach()](#interceptor-attach)** 或 **[replace()](#interceptor-replace)** 了一个 + 你即将使用 **[NativeFunction](#nativefunction)** 调用的函数。 + 每当当前线程即将离开 JavaScript 运行时或调用 **[send()](#communication-send)** 时,挂起的更改都会自动刷新。 + 这包括构建在 **[send()](#communication-send)** 之上的任何 API,例如从 **[RPC](#rpc-exports)** 方法返回时, + 以及调用 **[console](#console)** API 上的任何方法。 + ++ `Interceptor.breakpointKind`: 指定用于非内联钩子的断点类型的字符串。仅在 Barebone 后端可用。 + + 默认为 'soft',即软件断点。将其设置为 'hard' 以使用硬件断点。 + + +### Stalker + ++ `Stalker.exclude(range)`: 将指定的内存 `range` 标记为排除,该对象具有 `base` 和 `size` 属性—— + 就像例如 [`Process.getModuleByName()`](#process-getmodulebyname) 返回的对象中的属性一样。 + + 这意味着当遇到对该范围内指令的调用时,Stalker 将不会跟踪执行。 + 因此,你将能够观察/修改进入的参数和返回的返回值,但不会看到其间发生的指令。 + + 对于提高性能和减少噪音很有用。 + ++ `Stalker.follow([threadId, options])`: 开始跟踪 `threadId`(如果省略,则为当前线程), + 可选地使用 `options` 启用事件。 + {: #stalker-follow} + + 例如: + +{% highlight js %} +const mainThread = Process.enumerateThreads()[0]; + +Stalker.follow(mainThread.id, { + events: { + call: true, // CALL instructions: yes please + + // Other events: + ret: false, // RET instructions + exec: false, // all instructions: not recommended as it's + // a lot of data + block: false, // block executed: coarse execution trace + compile: false // block compiled: useful for coverage + }, + + // + // Only specify one of the two following callbacks. + // (See note below.) + // + + // + // onReceive: Called with `events` containing a binary blob + // comprised of one or more GumEvent structs. + // See `gumevent.h` for details about the + // format. Use `Stalker.parse()` to examine the + // data. + // + //onReceive(events) { + //}, + // + + // + // onCallSummary: Called with `summary` being a key-value + // mapping of call target to number of + // calls, in the current time window. You + // would typically implement this instead of + // `onReceive()` for efficiency, i.e. when + // you only want to know which targets were + // called and how many times, but don't care + // about the order that the calls happened + // in. + // + //onCallSummary(summary) { + //}, + + // + // Advanced users: This is how you can plug in your own + // StalkerTransformer, where the provided + // function is called synchronously + // whenever Stalker wants to recompile + // a basic block of the code that's about + // to be executed by the stalked thread. + // + //transform(iterator) { + // let instruction = iterator.next(); + // + // const startAddress = instruction.address; + // const isAppCode = startAddress.compare(appStart) >= 0 && + // startAddress.compare(appEnd) === -1; + // + // /* + // * Need to be careful on ARM/ARM64 as we may disturb instruction sequences + // * that deal with exclusive stores. + // */ + // const canEmitNoisyCode = iterator.memoryAccess === 'open'; + // + // do { + // if (isAppCode && canEmitNoisyCode && instruction.mnemonic === 'ret') { + // iterator.putCmpRegI32('eax', 60); + // iterator.putJccShortLabel('jb', 'nope', 'no-hint'); + // + // iterator.putCmpRegI32('eax', 90); + // iterator.putJccShortLabel('ja', 'nope', 'no-hint'); + // + // iterator.putCallout(onMatch); + // + // iterator.putLabel('nope'); + // + // /* You may also use putChainingReturn() to insert an early return. */ + // } + // + // iterator.keep(); + // } while ((instruction = iterator.next()) !== null); + //}, + // + // The default implementation is just: + // + // while (iterator.next() !== null) + // iterator.keep(); + // + // The example above shows how you can insert your own code + // just before every `ret` instruction across any code + // executed by the stalked thread inside the app's own + // memory range. It inserts code that checks if the `eax` + // register contains a value between 60 and 90, and inserts + // a synchronous callout back into JavaScript whenever that + // is the case. The callback receives a single argument + // that gives it access to the CPU registers, and it is + // also able to modify them. + // + // function onMatch (context) { + // console.log('Match! pc=' + context.pc + + // ' rax=' + context.rax.toInt32()); + // } + // + // Note that not calling keep() will result in the + // instruction getting dropped, which makes it possible + // for your transform to fully replace certain instructions + // when this is desirable. + // + + // + // Want better performance? Write the callbacks in C: + // + // /* + // * const cm = new CModule(\` + // * + // * #include + // * + // * static void on_ret (GumCpuContext * cpu_context, + // * gpointer user_data); + // * + // * void + // * transform (GumStalkerIterator * iterator, + // * GumStalkerOutput * output, + // * gpointer user_data) + // * { + // * cs_insn * insn; + // * + // * while (gum_stalker_iterator_next (iterator, &insn)) + // * { + // * if (insn->id == X86_INS_RET) + // * { + // * gum_x86_writer_put_nop (output->writer.x86); + // * gum_stalker_iterator_put_callout (iterator, + // * on_ret, NULL, NULL); + // * } + // * + // * gum_stalker_iterator_keep (iterator); + // * } + // * } + // * + // * static void + // * on_ret (GumCpuContext * cpu_context, + // * gpointer user_data) + // * { + // * printf ("on_ret!\n"); + // * } + // * + // * void + // * process (const GumEvent * event, + // * GumCpuContext * cpu_context, + // * gpointer user_data) + // * { + // * switch (event->type) + // * { + // * case GUM_CALL: + // * break; + // * case GUM_RET: + // * break; + // * case GUM_EXEC: + // * break; + // * case GUM_BLOCK: + // * break; + // * case GUM_COMPILE: + // * break; + // * default: + // * break; + // * } + // * } + // * `); + // */ + // + //transform: cm.transform, + //onEvent: cm.process, + //data: ptr(1337) /* user_data */ + // + // You may also use a hybrid approach and only write + // some of the callouts in C. + // +}); +{% endhighlight %} + +
+
性能注意事项
+

+ 提供的回调对性能有重大影响。如果你只需要定期调用摘要但不关心原始事件,或者反过来, + 请确保省略你不需要的回调;即避免将你的逻辑放在 onCallSummary 中并将 + onReceive 留在那儿作为一个空回调。 +

+

+ 另请注意,Stalker 可以与 **[CModule](#cmodule)** 结合使用,这意味着回调可以用 C 实现。 +

+
+ ++ `Stalker.unfollow([threadId])`: 停止跟踪 `threadId`(如果省略,则为当前线程)。 + {: #stalker-unfollow} + ++ `Stalker.parse(events[, options])`: 解析 GumEvent 二进制 blob,可选地使用 `options` 自定义输出。 + + 例如: + +{% highlight js %} + onReceive(events) { + console.log(Stalker.parse(events, { + annotate: true, // to display the type of event + stringify: true + // to format pointer values as strings instead of `NativePointer` + // values, i.e. less overhead if you're just going to `send()` the + // thing not actually parse the data agent-side + })); + }, +{% endhighlight %} + ++ `Stalker.flush()`: 刷新任何缓冲的事件。当你不想等到下一个 [`Stalker.queueDrainInterval`](#stalker-queuedraininterval) tick 时很有用。 + {: #stalker-flush} + ++ `Stalker.garbageCollect()`: 在 [`Stalker#unfollow`](#stalker-unfollow) 之后的安全点释放累积的内存。 + 这是为了避免刚刚取消跟踪的线程正在执行其最后指令的竞争条件所必需的。 + ++ `Stalker.invalidate(address)`: 使当前线程针对给定基本块的翻译代码无效。 + 当提供转换回调并希望动态调整给定基本块的插桩时很有用。 + 这比取消跟踪并重新跟踪线程要有效得多,后者会丢弃所有缓存的翻译并要求从头开始编译所有遇到的基本块。 + ++ `Stalker.invalidate(threadId, address)`: 使特定线程针对给定基本块的翻译代码无效。 + 当提供转换回调并希望动态调整给定基本块的插桩时很有用。 + 这比取消跟踪并重新跟踪线程要有效得多,后者会丢弃所有缓存的翻译并要求从头开始编译所有遇到的基本块。 + ++ `Stalker.addCallProbe(address, callback[, data])`: 当调用 `address` 时同步调用 `callback` + (签名见 [`Interceptor#attach#onEnter`](#interceptor-attach))。 + 返回一个 id,稍后可以将其传递给 [`Stalker#removeCallProbe`](#stalker-removecallprobe)。 + {: #stalker-addcallprobe} + + 也可以使用 **[CModule](#cmodule)** 在 C 中实现 `callback`,方法是指定 [`NativePointer`](#nativepointer) 而不是函数。签名: + + - `void onCall (GumCallSite * site, gpointer user_data)` + + In such cases, the third optional argument `data` may be a [`NativePointer`](#nativepointer) + whose value is passed to the callback as `user_data`. + ++ `Stalker.removeCallProbe`: 移除由 [`Stalker#addCallProbe`](#stalker-addcallprobe) 添加的调用探测。 + {: #stalker-removecallprobe} + ++ `Stalker.trustThreshold`: 一个整数,指定一段代码在被假定可以信任不会发生变异之前需要执行多少次。 + 指定 -1 表示不信任(慢),0 表示从一开始就信任代码,N 表示在执行 N 次后信任代码。默认为 1。 + ++ `Stalker.queueCapacity`: 一个整数,指定事件队列的容量(以事件数为单位)。默认为 16384 个事件。 + ++ `Stalker.queueDrainInterval`: 一个整数,指定每次耗尽事件队列之间的时间(以毫秒为单位)。 + 默认为 250 毫秒,这意味着事件队列每秒耗尽四次。你也可以将此属性设置为零以禁用定期耗尽, + 而是当你希望耗尽队列时调用 [`Stalker.flush()`](#stalker-flush)。 + {: #stalker-queuedraininterval} + + +### ObjC + +
+
Moved
+

+ 从 Frida 17 开始,此runtime bridge不再包含在 Frida 的 GumJS 运行时中,可以通过运行以下命令获取:`npm install frida-objc-bridge`。 +
+ 像这样将其导入到你的代理中:
+ `import ObjC from 'frida-objc-bridge';`
+ + 目前,在 Frida REPL 加载的脚本以及 frida-trace 中不需要这样做。 +

+
+ ++ `ObjC.available`: 一个布尔值,指定当前进程是否加载了 Objective-C 运行时。 + 除非是这种情况,否则不要调用任何其他 `ObjC` 属性或方法。 + {: #objc-available} + ++ `ObjC.api`: 一个将函数名称映射到 [`NativeFunction`](#nativefunction) 实例的对象, + 用于直接访问大部分 Objective-C 运行时 API。 + ++ `ObjC.classes`: 一个将类名映射到每个当前注册类的 [`ObjC.Object`](#objc-object) JavaScript 绑定的对象。 + 你可以通过使用点符号并将冒号替换为下划线来与对象交互,即: + `[NSString stringWithString:@"Hello World"]` + 变成 + `const { NSString } = ObjC.classes; NSString.stringWithString_("Hello World");`。 + 注意方法名后面的下划线。有关更多详细信息,请参阅 iOS 示例部分。 + {: #objc-classes} + ++ `ObjC.protocols`: 一个将协议名称映射到每个当前注册协议的 [`ObjC.Protocol`](#objc-protocol) JavaScript 绑定的对象。 + ++ `ObjC.mainQueue`: 主线程的 GCD 队列 + ++ `ObjC.schedule(queue, work)`: 在 `queue` 指定的 GCD 队列上调度 JavaScript 函数 `work`。 + 在调用 `work` 之前创建一个 `NSAutoreleasePool`,并在返回时清理。 + +{% highlight js %} +const { NSSound } = ObjC.classes; /* macOS */ +ObjC.schedule(ObjC.mainQueue, () => { + const sound = NSSound.alloc().initWithContentsOfFile_byReference_("/Users/oleavr/.Trash/test.mp3", true); + sound.play(); +}); +{% endhighlight %} + ++ new ObjC.Object(handle[, protocol]): 给定 `handle` 处的现有对象(一个 **[NativePointer](#nativepointer)**), + 创建一个 JavaScript 绑定。如果你想将 `handle` 视为仅实现特定协议的对象,你也可以指定 `protocol` 参数。 + +{% highlight js %} +Interceptor.attach(myFunction.implementation, { + onEnter(args) { + // ObjC: args[0] = self, args[1] = selector, args[2-n] = arguments + const myString = new ObjC.Object(args[2]); + console.log("String argument: " + myString.toString()); + } +}); +{% endhighlight %} + +> 此对象具有一些特殊属性: +> +> - `$kind`: 指定 `instance`、`class` 或 `meta-class` 的字符串 +> - `$super`: 一个 **[ObjC.Object](#objc-object)** 实例,用于链接到超类方法实现 +> - `$superClass`: 作为 **[ObjC.Object](#objc-object)** 实例的超类 +> - `$class`: 此对象的类作为 **[ObjC.Object](#objc-object)** 实例 +> - `$className`: 包含此对象类名的字符串 +> - `$moduleName`: 包含此对象模块路径的字符串 +> - `$protocols`: 将协议名称映射到此对象符合的每个协议的 [`ObjC.Protocol`](#objc-protocol) 实例的对象 +> - `$methods`: 包含此对象类和父类公开的本机方法名称的数组 +> - `$ownMethods`: 包含此对象类公开的本机方法名称的数组,不包括父类 +> - `$ivars`: 将每个实例变量名称映射到其当前值的对象,允许你通过访问和赋值来读取和写入每个变量 +> +> 还有一个 `equals(other)` 方法,用于检查两个实例是否引用同一个底层对象。 +> +> 请注意,所有方法包装器都提供了一个 `clone(options)` API,用于创建具有自定义 **[NativeFunction](#nativefunction)** 选项的新方法包装器。 + ++ `new ObjC.Protocol(handle)`: 给定 `handle` 处的现有协议(一个 **[NativePointer](#nativepointer)**), + 创建一个 JavaScript 绑定。 + {: #objc-protocol} + ++ `new ObjC.Block(target[, options])`: 给定 `target` 处的现有块(一个 **[NativePointer](#nativepointer)**)创建一个 JavaScript 绑定, + 或者,要定义一个新块,`target` 应该是一个指定类型签名和每当块被调用时要调用的 JavaScript 函数的对象。 + 函数使用 `implementation` 键指定,签名通过 `types` 键或 `retType` 和 `argTypes` 键指定。 + 有关详细信息,请参阅 [`ObjC.registerClass()`](#objc-registerclass)。 + + 请注意,如果现有块缺少签名元数据,你可以调用 `declare(signature)`,其中 `signature` 是一个具有 `types` 键 + 或 `retType` 和 `argTypes` 键的对象,如上所述。 + + 你也可以提供一个 `options` 对象,其中包含 **[NativeFunction](#nativefunction)** 支持的相同选项, + 例如传递 `traps: 'all'` 以便在调用块时 [`Stalker.follow()`](#stalker-follow) 执行。 + + 最常见的用例是钩住现有块,对于期望两个参数的块,它看起来像这样: + +{% highlight js %} +const pendingBlocks = new Set(); + +Interceptor.attach(..., { + onEnter(args) { + const block = new ObjC.Block(args[4]); + pendingBlocks.add(block); // Keep it alive + const appCallback = block.implementation; + block.implementation = (error, value) => { + // Do your logging here + const result = appCallback(error, value); + pendingBlocks.delete(block); + return result; + }; + } +}); +{% endhighlight %} + ++ `ObjC.implement(method, fn)`: 创建一个与 `method` 签名兼容的 JavaScript 实现, + 其中 JavaScript 函数 `fn` 用作实现。返回一个 [`NativeCallback`](#nativecallback), + 你可以将其分配给 ObjC 方法的 `implementation` 属性。 + +{% highlight js %} +const NSSound = ObjC.classes.NSSound; /* macOS */ +const oldImpl = NSSound.play.implementation; +NSSound.play.implementation = ObjC.implement(NSSound.play, (handle, selector) => { + return oldImpl(handle, selector); +}); + +const NSView = ObjC.classes.NSView; /* macOS */ +const drawRect = NSView['- drawRect:']; +const oldImpl = drawRect.implementation; +drawRect.implementation = ObjC.implement(drawRect, (handle, selector) => { + oldImpl(handle, selector); +}); +{% endhighlight %} + +> 由于 `implementation` 属性是一个 [`NativeFunction`](#nativefunction),因此也是一个 [`NativePointer`](#nativepointer), +> 你也可以使用 [`Interceptor`](#interceptor) 来钩住函数: + +{% highlight js %} +const { NSSound } = ObjC.classes; /* macOS */ +Interceptor.attach(NSSound.play.implementation, { + onEnter() { + send("[NSSound play]"); + } +}); +{% endhighlight %} + ++ `ObjC.registerProxy(properties)`: 创建一个旨在充当目标对象代理的新类,其中 `properties` 是一个指定以下内容的对象: + + - `protocols`: (可选) 此类符合的协议数组。 + - `methods`: (可选) 指定要实现的方法的对象。 + - `events`: (可选) 指定用于获取有关事件通知的回调的对象: + - `dealloc()`: 在对象被释放后立即调用。这是你可以清理任何关联状态的地方。 + - `forward(name)`: 使用 `name` 调用,指定我们要将调用转发到的方法名称。 + 这可能是你开始使用仅记录名称的临时回调的地方,以帮助你决定要覆盖哪些方法。 + +{% highlight js %} +const MyConnectionDelegateProxy = ObjC.registerProxy({ + protocols: [ObjC.protocols.NSURLConnectionDataDelegate], + methods: { + '- connection:didReceiveResponse:': function (conn, resp) { + /* fancy logging code here */ + /* this.data.foo === 1234 */ + this.data.target + .connection_didReceiveResponse_(conn, resp); + }, + '- connection:didReceiveData:': function (conn, data) { + /* other logging code here */ + this.data.target + .connection_didReceiveData_(conn, data); + } + }, + events: { + forward(name) { + console.log('*** forwarding: ' + name); + } + } +}); + +const method = ObjC.classes.NSURLConnection[ + '- initWithRequest:delegate:startImmediately:']; +Interceptor.attach(method.implementation, { + onEnter(args) { + args[3] = new MyConnectionDelegateProxy(args[3], { + foo: 1234 + }); + } +}); +{% endhighlight %} + ++ `ObjC.registerClass(properties)`: 创建一个新的 Objective-C 类,其中 `properties` 是一个指定以下内容的对象: + {: #objc-registerclass} + + - `name`: (可选) 指定类名的字符串;如果你不关心全局可见的名称并希望运行时为你自动生成一个,请省略此项。 + - `super`: (可选) 超类,或 *null* 以创建新的根类;省略以继承自 *NSObject*。 + - `protocols`: (可选) 此类符合的协议数组。 + - `methods`: (可选) 指定要实现的方法的对象。 + +{% highlight js %} +const MyConnectionDelegateProxy = ObjC.registerClass({ + name: 'MyConnectionDelegateProxy', + super: ObjC.classes.NSObject, + protocols: [ObjC.protocols.NSURLConnectionDataDelegate], + methods: { + '- init': function () { + const self = this.super.init(); + if (self !== null) { + ObjC.bind(self, { + foo: 1234 + }); + } + return self; + }, + '- dealloc': function () { + ObjC.unbind(this.self); + this.super.dealloc(); + }, + '- connection:didReceiveResponse:': function (conn, resp) { + /* this.data.foo === 1234 */ + }, + /* + * But those previous methods are declared assuming that + * either the super-class or a protocol we conform to has + * the same method so we can grab its type information. + * However, if that's not the case, you would write it + * like this: + */ + '- connection:didReceiveResponse:': { + retType: 'void', + argTypes: ['object', 'object'], + implementation(conn, resp) { + } + }, + /* Or grab it from an existing class: */ + '- connection:didReceiveResponse:': { + types: ObjC.classes + .Foo['- connection:didReceiveResponse:'].types, + implementation(conn, resp) { + } + }, + /* Or from an existing protocol: */ + '- connection:didReceiveResponse:': { + types: ObjC.protocols.NSURLConnectionDataDelegate + .methods['- connection:didReceiveResponse:'].types, + implementation(conn, resp) { + } + }, + /* Or write the signature by hand if you really want to: */ + '- connection:didReceiveResponse:': { + types: 'v32@0:8@16@24', + implementation(conn, resp) { + } + } + } +}); + +const proxy = MyConnectionDelegateProxy.alloc().init(); +/* use `proxy`, and later: */ +proxy.release(); +{% endhighlight %} + ++ `ObjC.registerProtocol(properties)`: 创建一个新的 Objective-C 协议,其中 `properties` 是一个指定以下内容的对象: + + - `name`: (可选) 指定协议名称的字符串;如果你不关心全局可见的名称并希望运行时为你自动生成一个,请省略此项。 + - `protocols`: (可选) 此协议包含的协议数组。 + - `methods`: (可选) 指定要声明的方法的对象。 + +{% highlight js %} +const MyDataDelegate = ObjC.registerProtocol({ + name: 'MyDataDelegate', + protocols: [ObjC.protocols.NSURLConnectionDataDelegate], + methods: { + /* You must specify the signature: */ + '- connection:didStuff:': { + retType: 'void', + argTypes: ['object', 'object'] + }, + /* Or grab it from a method of an existing class: */ + '- connection:didStuff:': { + types: ObjC.classes + .Foo['- connection:didReceiveResponse:'].types + }, + /* Or from an existing protocol method: */ + '- connection:didStuff:': { + types: ObjC.protocols.NSURLConnectionDataDelegate + .methods['- connection:didReceiveResponse:'].types + }, + /* Or write the signature by hand if you really want to: */ + '- connection:didStuff:': { + types: 'v32@0:8@16@24' + }, + /* You can also make a method optional (default is required): */ + '- connection:didStuff:': { + retType: 'void', + argTypes: ['object', 'object'], + optional: true + } + } +}); +{% endhighlight %} + ++ `ObjC.bind(obj, data)`: 将一些 JavaScript 数据绑定到 Objective-C 实例; + 有关示例,请参阅 [`ObjC.registerClass()`](#objc-registerclass)。 + ++ `ObjC.unbind(obj)`: 从 Objective-C 实例解绑先前关联的 JavaScript 数据; + 有关示例,请参阅 [`ObjC.registerClass()`](#objc-registerclass)。 + ++ `ObjC.getBoundData(obj)`: 从 Objective-C 对象中查找先前绑定的数据。 + ++ `ObjC.enumerateLoadedClasses([options, ]callbacks)`: 枚举当前已加载的类,其中 `callbacks` 是一个指定以下内容的对象: + {: #objc-enumerateloadedclasses} + + - `onMatch(name, owner)`: 为每个已加载的类调用,`name` 为类名字符串,`owner` 指定加载类的模块路径。 + 要获取给定类的 JavaScript 包装器,请执行:[`ObjC.classes[name]`](#objc-classes)。 + + - `onComplete()`: 当所有类都已枚举时调用。 + + 例如: + +{% highlight js %} +ObjC.enumerateLoadedClasses({ + onMatch(name, owner) { + console.log('onMatch:', name, owner); + }, + onComplete() { + } +}); +{% endhighlight %} + +可选的 `options` 参数是一个对象,你可以在其中指定 `ownedBy` 属性以将枚举限制为给定 [`ModuleMap`](#modulemap) 中的模块。 + +例如: + +{% highlight js %} +const appModules = new ModuleMap(isAppModule); +ObjC.enumerateLoadedClasses({ ownedBy: appModules }, { + onMatch(name, owner) { + console.log('onMatch:', name, owner); + }, + onComplete() { + } +}); + +function isAppModule(m) { + return !/^\/(usr\/lib|System|Developer)\//.test(m.path); +} +{% endhighlight %} + ++ `ObjC.enumerateLoadedClassesSync([options])`: [`enumerateLoadedClasses()`](#objc-enumerateloadedclasses) 的同步版本, + 返回一个将所有者模块映射到类名数组的对象。 + + 例如: + +{% highlight js %} +const appModules = new ModuleMap(isAppModule); +const appClasses = ObjC.enumerateLoadedClassesSync({ ownedBy: appModules }); +console.log('appClasses:', JSON.stringify(appClasses)); + +function isAppModule(m) { + return !/^\/(usr\/lib|System|Developer)\//.test(m.path); +} +{% endhighlight %} + ++ `ObjC.choose(specifier, callbacks)`: 通过扫描堆来枚举与 `specifier` 匹配的类的活动实例。 + `specifier` 是类选择器或指定类选择器和所需选项的对象。 + 类选择器是类的 **[ObjC.Object](#objc-object)**,例如 *ObjC.classes.UIButton*。 + 当传递一个对象作为说明符时,你应该提供带有类选择器的 `class` 字段,以及一个布尔值 `subclasses` 字段, + 指示你是否也对匹配给定类选择器的子类感兴趣。默认值是也包括子类。 + `callbacks` 参数是一个指定以下内容的对象: + {: #objc-choose} + + - `onMatch(instance)`: 为找到的每个活动实例调用一次,带有一个即用型 `instance`, + 就像你调用了 [`new ObjC.Object(ptr("0x1234"))`](#objc-object) 一样, + 知道这个特定的 Objective-C 实例位于 *0x1234*。 + + 此函数可能会返回字符串 `stop` 以提前取消枚举。 + + - `onComplete()`: 当所有实例都已枚举时调用 + ++ `ObjC.chooseSync(specifier)`: [`choose()`](#objc-choose) 的同步版本,它将实例作为数组返回。 + ++ `ObjC.selector(name)`: 将 JavaScript 字符串 `name` 转换为选择器 + ++ `ObjC.selectorAsString(sel)`: 将选择器 `sel` 转换为 JavaScript 字符串 + + +### Java + +
+
Moved
+

+ 从 Frida 17 开始,此runtime bridge不再包含在 Frida 的 GumJS 运行时中,可以通过运行以下命令获取:`npm install frida-java-bridge`。 +
+ + 像这样将其导入到你的代理中:
+ `import Java from 'frida-java-bridge';`
+ + 目前,在 Frida REPL 加载的脚本以及 frida-trace 中不需要这样做。 +

+
+ ++ `Java.available`: 一个布尔值,指定当前进程是否加载了 Java VM,即 Dalvik 或 ART。 + 除非是这种情况,否则不要调用任何其他 `Java` 属性或方法。 + ++ `Java.androidVersion`: 一个字符串,指定我们正在运行的 Android 版本。 + ++ `ACC_PUBLIC`, + `ACC_PRIVATE`, + `ACC_PROTECTED`, + `ACC_STATIC`, + `ACC_FINAL`, + `ACC_SYNCHRONIZED`, + `ACC_BRIDGE`, + `ACC_VARARGS`, + `ACC_NATIVE`, + `ACC_ABSTRACT`, + `ACC_STRICT`, + `ACC_SYNTHETIC`: 方法标志常量,每个都是一个数字,用于例如 [`Java.backtrace()`](#java-backtrace)。 + ++ `Java.enumerateLoadedClasses(callbacks)`: 枚举当前已加载的类,其中 `callbacks` 是一个指定以下内容的对象: + {: #java-enumerateloadedclasses} + + - `onMatch(name, handle)`: 为每个已加载的类调用,带有 `name`,可以传递给 [`use()`](#java-use) 以获取 JavaScript 包装器。 + 你也可以将 `handle` [`Java.cast()`](#java-cast) 为 `java.lang.Class`。 + + - `onComplete()`: 当所有类都已枚举时调用。 + ++ `Java.enumerateLoadedClassesSync()`: [`enumerateLoadedClasses()`](#java-enumerateloadedclasses) 的同步版本, + 它将类名作为数组返回。 + ++ `Java.enumerateClassLoaders(callbacks)`: 枚举 Java VM 中存在的类加载器,其中 `callbacks` 是一个指定以下内容的对象: + {: #java-enumerateclassloaders} + + - `onMatch(loader)`: 为每个类加载器调用,带有 `loader`,它是特定 `java.lang.ClassLoader` 的包装器。 + + - `onComplete()`: 当所有类加载器都已枚举时调用。 + + 你可以将此类加载器传递给 `Java.ClassFactory.get()` 以便能够在指定的类加载器上 [`.use()`](#java-use) 类。 + ++ `Java.enumerateClassLoadersSync()`: [`enumerateClassLoaders()`](#java-enumerateclassloaders) 的同步版本, + 它将类加载器作为数组返回。 + ++ `Java.enumerateMethods(query)`: 枚举与 `query` 匹配的方法,指定为 `"class!method"`,允许使用 glob。 + 也可以后缀 `/` 和一个或多个修饰符: + + - `i`: 不区分大小写的匹配。 + - `s`: 包括方法签名,例如 `"putInt"` 变成 `"putInt(java.lang.String, int): void"`。 + - `u`: 仅限用户定义的类,忽略系统类。 + +{% highlight js %} +Java.perform(() => { + const groups = Java.enumerateMethods('*youtube*!on*') + console.log(JSON.stringify(groups, null, 2)); +}); +{% endhighlight %} + +{% highlight json %} +[ + { + "loader": "", + "classes": [ + { + "name": "com.google.android.apps.youtube.app.watch.nextgenwatch.ui.NextGenWatchLayout", + "methods": [ + "onAttachedToWindow", + "onDetachedFromWindow", + "onFinishInflate", + "onInterceptTouchEvent", + "onLayout", + "onMeasure", + "onSizeChanged", + "onTouchEvent", + "onViewRemoved" + ] + }, + { + "name": "com.google.android.apps.youtube.app.search.suggest.YouTubeSuggestionProvider", + "methods": [ + "onCreate" + ] + }, + { + "name": "com.google.android.libraries.youtube.common.ui.YouTubeButton", + "methods": [ + "onInitializeAccessibilityNodeInfo" + ] + }, + … + ] + } +] +{% endhighlight %} + ++ `Java.scheduleOnMainThread(fn)`: 在 VM 的主线程上运行 `fn`。 + ++ `Java.perform(fn)`: 确保当前线程已附加到 VM 并调用 `fn`。(这在来自 Java 的回调中不是必需的。) + 如果应用程序的类加载器尚不可用,将推迟调用 `fn`。 + 如果不需要访问应用程序的类,请使用 [`Java.performNow()`](#java-performnow)。 + {: #java-perform} + +{% highlight js %} +Java.perform(() => { + const Activity = Java.use('android.app.Activity'); + Activity.onResume.implementation = function () { + send('onResume() got called! Let\'s call the original implementation'); + this.onResume(); + }; +}); +{% endhighlight %} + ++ `Java.performNow(fn)`: 确保当前线程已附加到 VM 并调用 `fn`。(这在来自 Java 的回调中不是必需的。) + {: #java-performnow} + ++ `Java.use(className)`: 动态获取 `className` 的 JavaScript 包装器,你可以通过对其调用 `$new()` 来实例化对象以调用构造函数。 + 在实例上调用 `$dispose()` 以显式清理它(或等待 JavaScript 对象被垃圾回收,或脚本被卸载)。 + 静态和非静态方法均可用,你甚至可以替换方法实现并从中抛出异常: + {: #java-use} + +{% highlight js %} +Java.perform(() => { + const Activity = Java.use('android.app.Activity'); + const Exception = Java.use('java.lang.Exception'); + Activity.onResume.implementation = function () { + throw Exception.$new('Oh noes!'); + }; +}); +{% endhighlight %} + +> 默认情况下使用应用程序的类加载器,但你可以通过将不同的加载器实例分配给 `Java.classFactory.loader` 来对此进行自定义。 +> +> 请注意,所有方法包装器都提供了一个 `clone(options)` API,用于创建具有自定义 **[NativeFunction](#nativefunction)** 选项的新方法包装器。 + ++ `Java.openClassFile(filePath)`: 打开 `filePath` 处的 .dex 文件,返回具有以下方法的对象: + {: #java-openclassfile} + + - `load()`: 将包含的类加载到 VM 中。 + + - `getClassNames()`: 获取可用类名的数组。 + ++ `Java.choose(className, callbacks)`: 通过扫描 Java 堆来枚举 `className` 类的活动实例,其中 `callbacks` 是一个指定以下内容的对象: + {: #java-choose} + + - `onMatch(instance)`: 为找到的每个活动实例调用,带有一个即用型 `instance`, + 就像你用这个特定实例的原始句柄调用了 [`Java.cast()`](#java-cast) 一样。 + + 此函数可能会返回字符串 `stop` 以提前取消枚举。 + + - `onComplete()`: 当所有实例都已枚举时调用 + ++ `Java.retain(obj)`: 复制 JavaScript 包装器 `obj` 以便在替换方法之外稍后使用。 + {: #java-retain} + +{% highlight js %} +Java.perform(() => { + const Activity = Java.use('android.app.Activity'); + let lastActivity = null; + Activity.onResume.implementation = function () { + lastActivity = Java.retain(this); + this.onResume(); + }; +}); +{% endhighlight %} + ++ Java.cast(handle, klass): 给定 [`Java.use()`](#java-use) 返回的给定类 `klass` 的 `handle` 处的现有实例, + 创建一个 JavaScript 包装器。 + 这样的包装器也有一个 `class` 属性用于获取其类的包装器,以及一个 `$className` 属性用于获取其类名的字符串表示形式。 + +{% highlight js %} +const Activity = Java.use('android.app.Activity'); +const activity = Java.cast(ptr('0x1234'), Activity); +{% endhighlight %} + ++ Java.array(type, elements): 从 JavaScript 数组 `elements` 创建一个具有指定 `type` 元素的 Java 数组。 + 生成的 Java 数组的行为类似于 JS 数组,但可以通过引用传递给 Java API,以允许它们修改其内容。 + +{% highlight js %} +const values = Java.array('int', [ 1003, 1005, 1007 ]); + +const JString = Java.use('java.lang.String'); +const str = JString.$new(Java.array('byte', [ 0x48, 0x65, 0x69 ])); +{% endhighlight %} + ++ `Java.backtrace([options])`: 为当前线程生成回溯。 + {: #java-backtrace} + + 可选的 `options` 参数是一个对象,可能包含以下一些键: + + - `limit`: 向上遍历堆栈的帧数,作为一个数字。 + 默认为 16。 + + 返回一个具有以下属性的对象: + + - `id`: 可用于对相同回溯进行重复数据删除的 ID,作为字符串。 + - `frames`: 堆栈帧。包含以下属性的对象数组: + + - `signature`: 堆栈帧签名作为字符串,例如 + `Landroid/os/Looper;,loopOnce,(Landroid/os/Looper;JI)Z` + - `origin`: 代码来源,即指定文件系统路径的字符串。在 Android 上,这是 `.dex` 的路径。 + - `className`: 方法所属的类名,作为字符串,例如 + `android.os.Looper` + - `methodName`: 方法名作为字符串,例如 `loopOnce` + - `methodFlags`: 方法标志作为数字,例如 + `Java.ACC_PUBLIC | Java.ACC_STATIC` + - `fileName`: 源文件名作为字符串,例如 `Looper.java` + - `lineNumber`: 源行号作为数字,例如 `201` + ++ `Java.isMainThread()`: 确定调用者是否在主线程上运行。 + ++ `Java.registerClass(spec)`: 创建一个新的 Java 类并返回它的包装器,其中 `spec` 是一个包含以下内容的对象: + {: #java-registerclass} + + - `name`: 指定类名的字符串。 + - `superClass`: (可选) 超类。省略以继承自 `java.lang.Object`。 + - `implements`: (可选) 此类实现的接口数组。 + - `fields`: (可选) 指定要公开的每个字段的名称和类型的对象。 + - `methods`: (可选) 指定要实现的方法的对象。 + +{% highlight js %} +const SomeBaseClass = Java.use('com.example.SomeBaseClass'); +const X509TrustManager = Java.use('javax.net.ssl.X509TrustManager'); + +const MyTrustManager = Java.registerClass({ + name: 'com.example.MyTrustManager', + implements: [X509TrustManager], + methods: { + checkClientTrusted(chain, authType) { + }, + checkServerTrusted(chain, authType) { + }, + getAcceptedIssuers() { + return []; + }, + } +}); + +const MyWeirdTrustManager = Java.registerClass({ + name: 'com.example.MyWeirdTrustManager', + superClass: SomeBaseClass, + implements: [X509TrustManager], + fields: { + description: 'java.lang.String', + limit: 'int', + }, + methods: { + $init() { + console.log('Constructor called'); + }, + checkClientTrusted(chain, authType) { + console.log('checkClientTrusted'); + }, + checkServerTrusted: [{ + returnType: 'void', + argumentTypes: ['[Ljava.security.cert.X509Certificate;', 'java.lang.String'], + implementation(chain, authType) { + console.log('checkServerTrusted A'); + } + }, { + returnType: 'java.util.List', + argumentTypes: ['[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'], + implementation(chain, authType, host) { + console.log('checkServerTrusted B'); + return null; + } + }], + getAcceptedIssuers() { + console.log('getAcceptedIssuers'); + return []; + }, + } +}); +{% endhighlight %} + ++ `Java.deoptimizeEverything()`: 强制 VM 使用其解释器执行所有操作。 + 这对于防止优化在某些情况下绕过方法钩子是必要的,并允许使用 ART 的 Instrumentation API 来跟踪运行时。 + ++ `Java.deoptimizeBootImage()`: 类似于 Java.deoptimizeEverything(),但仅取消优化引导映像代码。 + 与 `dalvik.vm.dex2oat-flags --inline-max-code-units=0` 一起使用以获得最佳效果。 + ++ `Java.vm`: 具有以下方法的对象: + + - `perform(fn)`: 确保当前线程已附加到 VM 并调用 `fn`。(这在来自 Java 的回调中不是必需的。) + + - `getEnv()`: 获取当前线程的 `JNIEnv` 的包装器。如果当前线程未附加到 VM,则抛出异常。 + + - `tryGetEnv()`: 尝试获取当前线程的 `JNIEnv` 的包装器。如果当前线程未附加到 VM,则返回 `null`。 + ++ `Java.classFactory`: 用于实现例如 [`Java.use()`](#java-use) 的默认类工厂。使用应用程序的主类加载器。 + ++ `Java.ClassFactory`: 具有以下属性的类: + + + `get(classLoader)`: 获取给定类加载器的类工厂实例。 + 在幕后使用的默认类工厂仅与应用程序的主类加载器交互。 + 其他类加载器可以通过 `Java.enumerateClassLoaders()` 发现并通过此 API 进行交互。 + + - `loader`: 只读属性,提供当前正在使用的类加载器的包装器。 + 对于默认类工厂,这是由第一次调用 [`Java.perform()`](#java-perform) 更新的。 + + - `cacheDir`: 包含当前正在使用的缓存目录路径的字符串。 + 对于默认类工厂,这是由第一次调用 [`Java.perform()`](#java-perform) 更新的。 + + - `tempFileNaming`: 指定用于临时文件的命名约定的对象。默认为 `{ prefix: 'frida', suffix: 'dat' }`。 + + - `use(className)`: 像 [`Java.use()`](#java-use) 但用于特定的类加载器。 + + - `openClassFile(filePath)`: 像 [`Java.openClassFile()`](#java-openclassfile) 但用于特定的类加载器。 + + - `choose(className, callbacks)`: 像 [`Java.choose()`](#java-choose) 但用于特定的类加载器。 + + - `retain(obj)`: 像 [`Java.retain()`](#java-retain) 但用于特定的类加载器。 + + - `cast(handle, klass)`: 像 [`Java.cast()`](#java-cast) 但用于特定的类 + loader. + + - `array(type, elements)`: 像 [`Java.array()`](#java-array) 但用于特定的类加载器。 + + - `registerClass(spec)`: 像 [`Java.registerClass()`](#java-registerclass) 但用于特定的类加载器。 + +--- + +## CPU 指令 + + +### Instruction + ++ `Instruction.parse(target)`: 解析内存中 `target` 地址处的指令,由 [`NativePointer`](#nativepointer) 表示。 + 请注意,在 32 位 ARM 上,对于 ARM 函数,此地址的最低有效位必须设置为 0,对于 Thumb 函数,必须设置为 1。 + 如果你从 Frida API(例如 [`Module#getExportByName()`](#module-getexportbyname))获取地址,Frida 会为你处理此细节。 + + 返回的对象具有以下字段: + + - `address`: 此指令的地址 (EIP),作为 [`NativePointer`](#nativepointer) + - `next`: 指向下一条指令的指针,因此你可以 `parse()` 它 + - `size`: 此指令的大小 + - `mnemonic`: 指令助记符的字符串表示形式 + - `opStr`: 指令操作数的字符串表示形式 + - `operands`: 描述每个操作数的对象数组,每个对象至少指定 `type` 和 `value`, + 但也可能根据体系结构指定其他属性 + - `regsRead`: 此指令隐式读取的寄存器名称数组 + - `regsWritten`: 此指令隐式写入的寄存器名称数组 + - `groups`: 此指令所属的组名数组 + - `toString()`: 转换为人类可读的字符串 + + 有关 `operands` 和 `groups` 的详细信息,请查阅你的体系结构的 **[Capstone](http://www.capstone-engine.org/)** 文档。 + + +### X86Writer + ++ `new X86Writer(codeAddress[, { pc: ptr('0x1234') }])`: 创建一个新的代码编写器, + 用于生成直接写入 `codeAddress` 处内存的 x86 机器代码,`codeAddress` 指定为 **[NativePointer](#nativepointer)**。 + 第二个参数是一个可选的选项对象,可以在其中指定初始程序计数器,这在生成代码到暂存缓冲区时很有用。 + 这在 iOS 上使用 [`Memory.patchCode()`](#memory-patchcode) 时至关重要,因为它可能会为你提供一个临时位置, + 稍后会将其映射到预期内存位置的内存中。 + +- `reset(codeAddress[, { pc: ptr('0x1234') }])`: 回收实例 + +- `dispose()`: 立即清理内存 + +- `flush()`: 解析标签引用并将挂起的数据写入内存。完成代码生成后,你应该始终调用一次。 + 通常也希望在不相关的代码片段之间执行此操作,例如一次生成多个函数时。 + +- `base`: 输出的第一个字节的内存位置,作为 **[NativePointer](#nativepointer)** + +- `code`: 输出的下一个字节的内存位置,作为 **[NativePointer](#nativepointer)** + +- `pc`: 输出的下一个字节处的程序计数器,作为 **[NativePointer](#nativepointer)** + +- `offset`: 当前偏移量作为 JavaScript 数字 + +- `putLabel(id)`: 在当前位置放置一个标签,其中 `id` 是一个可以在过去和将来的 `put*Label()` 调用中引用的字符串 + {: #x86writer-putlabel} + +- `putCallAddressWithArguments(func, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码, + `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或 + **[NativePointer](#nativepointer)**。 + +- `putCallAddressWithAlignedArguments(func, args)`: 与上面类似,但也确保参数列表在 16 字节边界上对齐 + +- `putCallRegWithArguments(reg, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码, + `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或 + **[NativePointer](#nativepointer)**。 + +- `putCallRegWithAlignedArguments(reg, args)`: 与上面类似,但也确保参数列表在 16 字节边界上对齐 + +- `putCallRegOffsetPtrWithArguments(reg, offset, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码, + `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或 + **[NativePointer](#nativepointer)**。 + +- `putCallAddress(address)`: 放置一个 CALL 指令 + +- `putCallReg(reg)`: 放置一个 CALL 指令 + +- `putCallRegOffsetPtr(reg, offset)`: 放置一个 CALL 指令 + +- `putCallIndirect(addr)`: 放置一个 CALL 指令 + +- `putCallIndirectLabel(labelId)`: 放置一个引用 `labelId` 的 CALL 指令, + `labelId` 由过去或将来的 [`putLabel()`](#x86writer-putlabel) 定义 + +- `putCallNearLabel(labelId)`: 放置一个引用 `labelId` 的 CALL 指令, + `labelId` 由过去或将来的 [`putLabel()`](#x86writer-putlabel) 定义 + +- `putLeave()`: 放置一个 LEAVE 指令 + +- `putRet()`: 放置一个 RET 指令 + +- `putRetImm(immValue)`: 放置一个 RET 指令 + +- `putJmpAddress(address)`: 放置一个 JMP 指令 + +- `putJmpShortLabel(labelId)`: 放置一个引用 `labelId` 的 JMP 指令, + `labelId` 由过去或将来的 [`putLabel()`](#x86writer-putlabel) 定义 + +- `putJmpNearLabel(labelId)`: 放置一个引用 `labelId` 的 JMP 指令, + `labelId` 由过去或将来的 [`putLabel()`](#x86writer-putlabel) 定义 + +- `putJmpReg(reg)`: 放置一个 JMP 指令 + +- `putJmpRegPtr(reg)`: 放置一个 JMP 指令 + +- `putJmpRegOffsetPtr(reg, offset)`: 放置一个 JMP 指令 + +- `putJmpNearPtr(address)`: 放置一个 JMP 指令 + +- `putJccShort(instructionId, target, hint)`: 放置一个 JCC 指令 + +- `putJccNear(instructionId, target, hint)`: 放置一个 JCC 指令 + +- `putJccShortLabel(instructionId, labelId, hint)`: 放置一个引用 `labelId` 的 JCC 指令, + `labelId` 由过去或将来的 [`putLabel()`](#x86writer-putlabel) 定义 + +- `putJccNearLabel(instructionId, labelId, hint)`: 放置一个引用 `labelId` 的 JCC 指令, + `labelId` 由过去或将来的 [`putLabel()`](#x86writer-putlabel) 定义 + +- `putAddRegImm(reg, immValue)`: 放置一个 ADD 指令 + +- `putAddRegReg(dstReg, srcReg)`: 放置一个 ADD 指令 + +- `putAddRegNearPtr(dstReg, srcAddress)`: 放置一个 ADD 指令 + +- `putSubRegImm(reg, immValue)`: 放置一个 SUB 指令 + +- `putSubRegReg(dstReg, srcReg)`: 放置一个 SUB 指令 + +- `putSubRegNearPtr(dstReg, srcAddress)`: 放置一个 SUB 指令 + +- `putIncReg(reg)`: 放置一个 INC 指令 + +- `putDecReg(reg)`: 放置一个 DEC 指令 + +- `putIncRegPtr(target, reg)`: 放置一个 INC 指令 + +- `putDecRegPtr(target, reg)`: 放置一个 DEC 指令 + +- `putLockXaddRegPtrReg(dstReg, srcReg)`: 放置一个 LOCK XADD 指令 + +- `putLockCmpxchgRegPtrReg(dstReg, srcReg)`: 放置一个 LOCK CMPXCHG 指令 + +- `putLockIncImm32Ptr(target)`: 放置一个 LOCK INC IMM32 指令 + +- `putLockDecImm32Ptr(target)`: 放置一个 LOCK DEC IMM32 指令 + +- `putAndRegReg(dstReg, srcReg)`: 放置一个 AND 指令 + +- `putAndRegU32(reg, immValue)`: 放置一个 AND 指令 + +- `putShlRegU8(reg, immValue)`: 放置一个 SHL 指令 + +- `putShrRegU8(reg, immValue)`: 放置一个 SHR 指令 + +- `putXorRegReg(dstReg, srcReg)`: 放置一个 XOR 指令 + +- `putMovRegReg(dstReg, srcReg)`: 放置一个 MOV 指令 + +- `putMovRegU32(dstReg, immValue)`: 放置一个 MOV 指令 + +- `putMovRegU64(dstReg, immValue)`: 放置一个 MOV 指令 + +- `putMovRegAddress(dstReg, address)`: 放置一个 MOV 指令 + +- `putMovRegPtrU32(dstReg, immValue)`: 放置一个 MOV 指令 + +- `putMovRegOffsetPtrU32(dstReg, dstOffset, immValue)`: 放置一个 MOV 指令 + +- `putMovRegPtrReg(dstReg, srcReg)`: 放置一个 MOV 指令 + +- `putMovRegOffsetPtrReg(dstReg, dstOffset, srcReg)`: 放置一个 MOV 指令 + +- `putMovRegRegPtr(dstReg, srcReg)`: 放置一个 MOV 指令 + +- `putMovRegRegOffsetPtr(dstReg, srcReg, srcOffset)`: 放置一个 MOV 指令 + +- `putMovRegBaseIndexScaleOffsetPtr(dstReg, baseReg, indexReg, scale, offset)`: 放置一个 MOV 指令 + +- `putMovRegNearPtr(dstReg, srcAddress)`: 放置一个 MOV 指令 + +- `putMovNearPtrReg(dstAddress, srcReg)`: 放置一个 MOV 指令 + +- `putMovFsU32PtrReg(fsOffset, srcReg)`: 放置一个 MOV FS 指令 + +- `putMovRegFsU32Ptr(dstReg, fsOffset)`: 放置一个 MOV FS 指令 + +- `putMovFsRegPtrReg(fsOffset, srcReg)`: 放置一个 MOV FS 指令 + +- `putMovRegFsRegPtr(dstReg, fsOffset)`: 放置一个 MOV FS 指令 + +- `putMovGsU32PtrReg(fsOffset, srcReg)`: 放置一个 MOV GS 指令 + +- `putMovRegGsU32Ptr(dstReg, fsOffset)`: 放置一个 MOV GS 指令 + +- `putMovGsRegPtrReg(gsOffset, srcReg)`: 放置一个 MOV GS 指令 + +- `putMovRegGsRegPtr(dstReg, gsOffset)`: 放置一个 MOV GS 指令 + +- `putMovqXmm0EspOffsetPtr(offset)`: 放置一个 MOVQ XMM0 ESP 指令 + +- `putMovqEaxOffsetPtrXmm0(offset)`: 放置一个 MOVQ EAX XMM0 指令 + +- `putMovdquXmm0EspOffsetPtr(offset)`: 放置一个 MOVDQU XMM0 ESP 指令 + +- `putMovdquEaxOffsetPtrXmm0(offset)`: 放置一个 MOVDQU EAX XMM0 指令 + +- `putLeaRegRegOffset(dstReg, srcReg, srcOffset)`: 放置一个 LEA 指令 + +- `putXchgRegRegPtr(leftReg, rightReg)`: 放置一个 XCHG 指令 + +- `putPushU32(immValue)`: 放置一个 PUSH 指令 + +- `putPushNearPtr(address)`: 放置一个 PUSH 指令 + +- `putPushReg(reg)`: 放置一个 PUSH 指令 + +- `putPopReg(reg)`: 放置一个 POP 指令 + +- `putPushImmPtr(immPtr)`: 放置一个 PUSH 指令 + +- `putPushax()`: 放置一个 PUSHAX 指令 + +- `putPopax()`: 放置一个 POPAX 指令 + +- `putPushfx()`: 放置一个 PUSHFX 指令 + +- `putPopfx()`: 放置一个 POPFX 指令 + +- `putSahf()`: 放置一个 SAHF 指令 + +- `putLahf()`: 放置一个 LAHF 指令 + +- `putTestRegReg(regA, regB)`: 放置一个 TEST 指令 + +- `putTestRegU32(reg, immValue)`: 放置一个 TEST 指令 + +- `putCmpRegI32(reg, immValue)`: 放置一个 CMP 指令 + +- `putCmpRegOffsetPtrReg(regA, offset, regB)`: 放置一个 CMP 指令 + +- `putCmpImmPtrImmU32(immPtr, immValue)`: 放置一个 CMP 指令 + +- `putCmpRegReg(regA, regB)`: 放置一个 CMP 指令 + +- `putClc()`: 放置一个 CLC 指令 + +- `putStc()`: 放置一个 STC 指令 + +- `putCld()`: 放置一个 CLD 指令 + +- `putStd()`: 放置一个 STD 指令 + +- `putCpuid()`: 放置一个 CPUID 指令 + +- `putLfence()`: 放置一个 LFENCE 指令 + +- `putRdtsc()`: 放置一个 RDTSC 指令 + +- `putPause()`: 放置一个 PAUSE 指令 + +- `putNop()`: 放置一个 NOP 指令 + +- `putBreakpoint()`: 放置一个特定于操作系统/体系结构的断点指令 + +- `putPadding(n)`: 放置 `n` 个保护指令 + +- `putNopPadding(n)`: 放置 `n` 个 NOP 指令 + +- `putFxsaveRegPtr(reg)`: 放置一个 FXSAVE 指令 + +- `putFxrstorRegPtr(reg)`: 放置一个 FXRSTOR 指令 + +- `putU8(value)`: 放置一个 uint8 + +- `putS8(value)`: 放置一个 int8 + +- `putBytes(data)`: 放置来自提供的 **[ArrayBuffer](#arraybuffer)** 的原始数据 + + +### X86Relocator + ++ `new X86Relocator(inputCode, output)`: 创建一个新的代码重定位器,用于将 x86 指令从一个内存位置复制到另一个内存位置, + 并注意相应地调整位置相关指令。 + 源地址由 `inputCode` 指定,这是一个 **[NativePointer](#nativepointer)**。 + 目的地由 `output` 给出,这是一个指向所需目标内存地址的 **[X86Writer](#x86writer)**。 + +- `reset(inputCode, output)`: 回收实例 + +- `dispose()`: 立即清理内存 + +- `input`: 到目前为止读取的最新 **[Instruction](#instruction)**。开始时为 `null`, + 并在每次调用 [`readOne()`](#x86relocator-readone) 时更改。 + +- `eob`: 布尔值,指示是否已到达块的末尾,即我们已到达任何类型的分支,如 CALL、JMP、BL、RET。 + +- `eoi`: 布尔值,指示是否已到达输入的末尾,例如我们已到达 JMP/B/RET,这是一条指令,其后可能有也可能没有有效代码。 + +- `readOne()`: 将下一条指令读入重定位器的内部缓冲区,并返回到目前为止读取的字节数,包括以前的调用。 + 你可以继续调用此方法以继续缓冲,或者立即调用 [`writeOne()`](#x86relocator-writeone) 或 [`skipOne()`](#x86relocator-skipone)。 + 或者,你可以缓冲直到所需的点,然后调用 [`writeAll()`](#x86relocator-writeall)。 + 当到达输入末尾时返回零,这意味着 `eoi` 属性现在为 `true`。 + {: #x86relocator-readone} + +- `peekNextWriteInsn()`: 查看要写入或跳过的下一条 **[Instruction](#instruction)** + +- `peekNextWriteSource()`: 查看要写入或跳过的下一条指令的地址 + +- `skipOne()`: 跳过本来要写入的下一条指令 + {: #x86relocator-skipone} + +- `skipOneNoLabel()`: 跳过本来要写入的下一条指令,但不使用标签供内部使用。 + 这破坏了分支到重定位范围内的位置的重定位,并且是针对所有分支都被重写(例如 Frida 的 **[Stalker](#stalker)**)的用例的优化。 + +- `writeOne()`: 写入下一条缓冲的指令 + {: #x86relocator-writeone} + +- `writeOneNoLabel()`: 写入下一条缓冲的指令,但不使用标签供内部使用。 + 这破坏了分支到重定位范围内的位置的重定位,并且是针对所有分支都被重写(例如 Frida 的 **[Stalker](#stalker)**)的用例的优化。 + +- `writeAll()`: 写入所有缓冲的指令 + {: #x86relocator-writeall} + + +### x86 enum types + +- Register: `xax` `xcx` `xdx` `xbx` `xsp` `xbp` `xsi` `xdi` `eax` `ecx` `edx` + `ebx` `esp` `ebp` `esi` `edi` `rax` `rcx` `rdx` `rbx` `rsp` `rbp` `rsi` + `rdi` `r8` `r9` `r10` `r11` `r12` `r13` `r14` `r15` `r8d` `r9d` `r10d` + `r11d` `r12d` `r13d` `r14d` `r15d` `xip` `eip` `rip` +- InstructionId: `jo` `jno` `jb` `jae` `je` `jne` `jbe` `ja` `js` `jns` `jp` + `jnp` `jl` `jge` `jle` `jg` `jcxz` `jecxz` `jrcxz` +- BranchHint: `no-hint` `likely` `unlikely` +- PointerTarget: `byte` `dword` `qword` + + +### ArmWriter + ++ `new ArmWriter(codeAddress[, { pc: ptr('0x1234') }])`: 创建一个新的代码编写器, + 用于生成直接写入 `codeAddress` 处内存的 ARM 机器代码,`codeAddress` 指定为 **[NativePointer](#nativepointer)**。 + 第二个参数是一个可选的选项对象,可以在其中指定初始程序计数器,这在生成代码到暂存缓冲区时很有用。 + 这在 iOS 上使用 [`Memory.patchCode()`](#memory-patchcode) 时至关重要,因为它可能会为你提供一个临时位置, + 稍后会将其映射到预期内存位置的内存中。 + +- `reset(codeAddress[, { pc: ptr('0x1234') }])`: 回收实例 + +- `dispose()`: 立即清理内存 + +- `flush()`: 解析标签引用并将挂起的数据写入内存。完成代码生成后,你应该始终调用一次。 + 通常也希望在不相关的代码片段之间执行此操作,例如一次生成多个函数时。 + +- `base`: 输出的第一个字节的内存位置,作为 **[NativePointer](#nativepointer)** + +- `code`: 输出的下一个字节的内存位置,作为 **[NativePointer](#nativepointer)** + +- `pc`: 输出的下一个字节处的程序计数器,作为 **[NativePointer](#nativepointer)** + +- `offset`: 当前偏移量作为 JavaScript 数字 + +- `skip(nBytes)`: 跳过 `nBytes` + +- `putLabel(id)`: 在当前位置放置一个标签,其中 `id` 是一个可以在过去和将来的 `put*Label()` 调用中引用的字符串 + {: #armwriter-putlabel} + +- `putCallAddressWithArguments(func, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码, + `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或 + **[NativePointer](#nativepointer)**。 + +- `putCallReg(reg)`: 放置一个 CALL 指令 + +- `putCallRegWithArguments(reg, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码, + `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或 + NativePointer 指定的立即值。 + +- `putBranchAddress(address)`: 放置分支/跳转到给定地址所需的代码 + +- `canBranchDirectlyBetween(from, to)`: 确定两个给定内存位置之间是否可以直接分支 + +- `putBImm(target)`: 放置一个 B 指令 + +- `putBCondImm(cc, target)`: 放置一个 B COND 指令 + +- `putBLabel(labelId)`: 放置一个引用 `labelId` 的 B 指令, + `labelId` 由过去或将来的 [`putLabel()`](#armwriter-putlabel) 定义 + +- `putBCondLabel(cc, labelId)`: 放置一个引用 `labelId` 的 B COND 指令, + `labelId` 由过去或将来的 [`putLabel()`](#armwriter-putlabel) 定义 + +- `putBlImm(target)`: 放置一个 BL 指令 + +- `putBlxImm(target)`: 放置一个 BLX 指令 + +- `putBlLabel(labelId)`: 放置一个引用 `labelId` 的 BL 指令, + `labelId` 由过去或将来的 [`putLabel()`](#armwriter-putlabel) 定义 + +- `putBxReg(reg)`: 放置一个 BX 指令 + +- `putBlReg(reg)`: 放置一个 BL 指令 + +- `putBlxReg(reg)`: 放置一个 BLX 指令 + +- `putRet()`: 放置一个 RET 指令 + +- `putVpushRange(firstReg, lastReg)`: 放置一个 VPUSH RANGE 指令 + +- `putVpopRange(firstReg, lastReg)`: 放置一个 VPOP RANGE 指令 + +- `putLdrRegAddress(reg, address)`: 放置一个 LDR 指令 + +- `putLdrRegU32(reg, val)`: 放置一个 LDR 指令 + +- `putLdrRegReg(dstReg, srcReg)`: 放置一个 LDR 指令 + +- `putLdrRegRegOffset(dstReg, srcReg, srcOffset)`: 放置一个 LDR 指令 + +- `putLdrCondRegRegOffset(cc, dstReg, srcReg, srcOffset)`: 放置一个 LDR COND 指令 + +- `putLdmiaRegMask(reg, mask)`: 放置一个 LDMIA MASK 指令 + +- `putLdmiaRegMaskWb(reg, mask)`: 放置一个 LDMIA MASK WB 指令 + +- `putStrRegReg(srcReg, dstReg)`: 放置一个 STR 指令 + +- `putStrRegRegOffset(srcReg, dstReg, dstOffset)`: 放置一个 STR 指令 + +- `putStrCondRegRegOffset(cc, srcReg, dstReg, dstOffset)`: 放置一个 STR COND 指令 + +- `putMovRegReg(dstReg, srcReg)`: 放置一个 MOV 指令 + +- `putMovRegRegShift(dstReg, srcReg, shift, shiftValue)`: 放置一个 MOV SHIFT 指令 + +- `putMovRegCpsr(reg)`: 放置一个 MOV CPSR 指令 + +- `putMovCpsrReg(reg)`: 放置一个 MOV CPSR 指令 + +- `putAddRegU16(dstReg, val)`: 放置一个 ADD U16 指令 + +- `putAddRegU32(dstReg, val)`: 放置一个 ADD 指令 + +- `putAddRegRegImm(dstReg, srcReg, immVal)`: 放置一个 ADD 指令 + +- `putAddRegRegReg(dstReg, srcReg1, srcReg2)`: 放置一个 ADD 指令 + +- `putAddRegRegRegShift(dstReg, srcReg1, srcReg2, shift, shiftValue)`: 放置一个 ADD SHIFT 指令 + +- `putSubRegU16(dstReg, val)`: 放置一个 SUB U16 指令 + +- `putSubRegU32(dstReg, val)`: 放置一个 SUB 指令 + +- `putSubRegRegImm(dstReg, srcReg, immVal)`: 放置一个 SUB 指令 + +- `putSubRegRegReg(dstReg, srcReg1, srcReg2)`: 放置一个 SUB 指令 + +- `putRsbRegRegImm(dstReg, srcReg, immVal)`: 放置一个 RSB 指令 + +- `putAndsRegRegImm(dstReg, srcReg, immVal)`: 放置一个 ANDS 指令 + +- `putCmpRegImm(dstReg, immVal)`: 放置一个 CMP 指令 + +- `putNop()`: 放置一个 NOP 指令 + +- `putBreakpoint()`: 放置一个特定于操作系统/体系结构的断点指令 + +- `putBrkImm(imm)`: 放置一个 BRK 指令 + +- `putInstruction(insn)`: 将原始指令作为 JavaScript 数字放置 + +- `putBytes(data)`: 放置来自提供的 **[ArrayBuffer](#arraybuffer)** 的原始数据 + + +### ArmRelocator + ++ `new ArmRelocator(inputCode, output)`: 创建一个新的代码重定位器,用于将 ARM 指令从一个内存位置复制到另一个内存位置, + 并注意相应地调整位置相关指令。 + 源地址由 `inputCode` 指定,这是一个 **[NativePointer](#nativepointer)**。 + 目的地由 `output` 给出,这是一个指向所需目标内存地址的 **[ArmWriter](#armwriter)**。 + +- `reset(inputCode, output)`: 回收实例 + +- `dispose()`: 立即清理内存 + +- `input`: 到目前为止读取的最新 **[Instruction](#instruction)**。开始时为 `null`, + 并在每次调用 [`readOne()`](#armrelocator-readone) 时更改。 + +- `eob`: 布尔值,指示是否已到达块的末尾,即我们已到达任何类型的分支,如 CALL、JMP、BL、RET。 + +- `eoi`: 布尔值,指示是否已到达输入的末尾,例如我们已到达 JMP/B/RET,这是一条指令,其后可能有也可能没有有效代码。 + +- `readOne()`: 将下一条指令读入重定位器的内部缓冲区,并返回到目前为止读取的字节数,包括以前的调用。 + 你可以继续调用此方法以继续缓冲,或者立即调用 [`writeOne()`](#armrelocator-writeone) 或 [`skipOne()`](#armrelocator-skipone)。 + 或者,你可以缓冲直到所需的点,然后调用 [`writeAll()`](#armrelocator-writeall)。 + 当到达输入末尾时返回零,这意味着 `eoi` 属性现在为 `true`。 + {: #armrelocator-readone} + +- `peekNextWriteInsn()`: 查看要写入或跳过的下一条 **[Instruction](#instruction)** + +- `peekNextWriteSource()`: 查看要写入或跳过的下一条指令的地址 + +- `skipOne()`: 跳过本来要写入的下一条指令 + {: #armrelocator-skipone} + +- `writeOne()`: 写入下一条缓冲的指令 + {: #armrelocator-writeone} + +- `writeAll()`: 写入所有缓冲的指令 + {: #armrelocator-writeall} + + +### ThumbWriter + ++ `new ThumbWriter(codeAddress[, { pc: ptr('0x1234') }])`: 创建一个新的代码编写器, + 用于生成直接写入 `codeAddress` 处内存的 ARM 机器代码,`codeAddress` 指定为 **[NativePointer](#nativepointer)**。 + 第二个参数是一个可选的选项对象,可以在其中指定初始程序计数器,这在生成代码到暂存缓冲区时很有用。 + 这在 iOS 上使用 [`Memory.patchCode()`](#memory-patchcode) 时至关重要,因为它可能会为你提供一个临时位置, + 稍后会将其映射到预期内存位置的内存中。 + +- `reset(codeAddress[, { pc: ptr('0x1234') }])`: 回收实例 + +- `dispose()`: 立即清理内存 + +- `flush()`: 解析标签引用并将挂起的数据写入内存。完成代码生成后,你应该始终调用一次。 + 通常也希望在不相关的代码片段之间执行此操作,例如一次生成多个函数时。 + +- `base`: 输出的第一个字节的内存位置,作为 **[NativePointer](#nativepointer)** + +- `code`: 输出的下一个字节的内存位置,作为 **[NativePointer](#nativepointer)** + +- `pc`: 输出的下一个字节处的程序计数器,作为 **[NativePointer](#nativepointer)** + +- `offset`: 当前偏移量作为 JavaScript 数字 + +- `skip(nBytes)`: 跳过 `nBytes` + +- `putLabel(id)`: 在当前位置放置一个标签,其中 `id` 是一个可以在过去和将来的 `put*Label()` 调用中引用的字符串 + {: #thumbwriter-putlabel} + +- `commitLabel(id)`: 提交对给定标签的第一个挂起引用,成功时返回 `true`。 + 如果尚未定义给定标签,或者没有更多对它的挂起引用,则返回 `false`。 + +- `putCallAddressWithArguments(func, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码, + `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或 + **[NativePointer](#nativepointer)**。 + +- `putCallRegWithArguments(reg, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码, + `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或 + **[NativePointer](#nativepointer)**。 + +- `putBranchAddress(address)`: 放置分支/跳转到给定地址所需的代码 + +- `canBranchDirectlyBetween(from, to)`: 确定两个给定内存位置之间是否可以直接分支 + +- `putBImm(target)`: 放置一个 B 指令 + +- `putBLabel(labelId)`: 放置一个引用 `labelId` 的 B 指令, + `labelId` 由过去或将来的 [`putLabel()`](#thumbwriter-putlabel) 定义 + +- `putBLabelWide(labelId)`: 放置一个 B WIDE 指令 + +- `putBxReg(reg)`: 放置一个 BX 指令 + +- `putBlImm(target)`: 放置一个 BL 指令 + +- `putBlLabel(labelId)`: 放置一个引用 `labelId` 的 BL 指令, + `labelId` 由过去或将来的 [`putLabel()`](#thumbwriter-putlabel) 定义 + +- `putBlxImm(target)`: 放置一个 BLX 指令 + +- `putBlxReg(reg)`: 放置一个 BLX 指令 + +- `putCmpRegImm(reg, immValue)`: 放置一个 CMP 指令 + +- `putBeqLabel(labelId)`: 放置一个引用 `labelId` 的 BEQ 指令, + `labelId` 由过去或将来的 [`putLabel()`](#thumbwriter-putlabel) 定义 + +- `putBneLabel(labelId)`: 放置一个引用 `labelId` 的 BNE 指令, + `labelId` 由过去或将来的 [`putLabel()`](#thumbwriter-putlabel) 定义 + +- `putBCondLabel(cc, labelId)`: 放置一个引用 `labelId` 的 B COND 指令, + `labelId` 由过去或将来的 [`putLabel()`](#thumbwriter-putlabel) 定义 + +- `putBCondLabelWide(cc, labelId)`: 放置一个 B COND WIDE 指令 + +- `putCbzRegLabel(reg, labelId)`: 放置一个引用 `labelId` 的 CBZ 指令, + `labelId` 由过去或将来的 [`putLabel()`](#thumbwriter-putlabel) 定义 + +- `putCbnzRegLabel(reg, labelId)`: 放置一个引用 `labelId` 的 CBNZ 指令, + `labelId` 由过去或将来的 [`putLabel()`](#thumbwriter-putlabel) 定义 + +- `putPushRegs(regs)`: 放置一个具有指定寄存器的 PUSH 指令, + 指定为 JavaScript 数组,其中每个元素是指定寄存器名称的字符串。 + +- `putPopRegs(regs)`: 放置一个具有指定寄存器的 POP 指令, + 指定为 JavaScript 数组,其中每个元素是指定寄存器名称的字符串。 + +- `putVpushRange(firstReg, lastReg)`: 放置一个 VPUSH RANGE 指令 + +- `putVpopRange(firstReg, lastReg)`: 放置一个 VPOP RANGE 指令 + +- `putLdrRegAddress(reg, address)`: 放置一个 LDR 指令 + +- `putLdrRegU32(reg, val)`: 放置一个 LDR 指令 + +- `putLdrRegReg(dstReg, srcReg)`: 放置一个 LDR 指令 + +- `putLdrRegRegOffset(dstReg, srcReg, srcOffset)`: 放置一个 LDR 指令 + +- `putLdrbRegReg(dstReg, srcReg)`: 放置一个 LDRB 指令 + +- `putVldrRegRegOffset(dstReg, srcReg, srcOffset)`: 放置一个 VLDR 指令 + +- `putLdmiaRegMask(reg, mask)`: 放置一个 LDMIA MASK 指令 + +- `putStrRegReg(srcReg, dstReg)`: 放置一个 STR 指令 + +- `putStrRegRegOffset(srcReg, dstReg, dstOffset)`: 放置一个 STR 指令 + +- `putMovRegReg(dstReg, srcReg)`: 放置一个 MOV 指令 + +- `putMovRegU8(dstReg, immValue)`: 放置一个 MOV 指令 + +- `putMovRegCpsr(reg)`: 放置一个 MOV CPSR 指令 + +- `putMovCpsrReg(reg)`: 放置一个 MOV CPSR 指令 + +- `putAddRegImm(dstReg, immValue)`: 放置一个 ADD 指令 + +- `putAddRegReg(dstReg, srcReg)`: 放置一个 ADD 指令 + +- `putAddRegRegReg(dstReg, leftReg, rightReg)`: 放置一个 ADD 指令 + +- `putAddRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 ADD 指令 + +- `putSubRegImm(dstReg, immValue)`: 放置一个 SUB 指令 + +- `putSubRegReg(dstReg, srcReg)`: 放置一个 SUB 指令 + +- `putSubRegRegReg(dstReg, leftReg, rightReg)`: 放置一个 SUB 指令 + +- `putSubRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 SUB 指令 + +- `putAndRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 AND 指令 + +- `putOrRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 OR 指令 + +- `putLslRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 LSL 指令 + +- `putLslsRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 LSLS 指令 + +- `putLsrsRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 LSRS 指令 + +- `putMrsRegReg(dstReg, srcReg)`: 放置一个 MRS 指令 + +- `putMsrRegReg(dstReg, srcReg)`: 放置一个 MSR 指令 + +- `putNop()`: 放置一个 NOP 指令 + +- `putBkptImm(imm)`: 放置一个 BKPT 指令 + +- `putBreakpoint()`: 放置一个特定于操作系统/体系结构的断点指令 + +- `putInstruction(insn)`: 将原始指令作为 JavaScript 数字放置 + +- `putInstructionWide(upper, lower)`: 从两个 JavaScript 数字值放置一个原始 Thumb-2 指令 + +- `putBytes(data)`: 放置来自提供的 **[ArrayBuffer](#arraybuffer)** 的原始数据 + + +### ThumbRelocator + ++ `new ThumbRelocator(inputCode, output)`: 创建一个新的代码重定位器,用于将 ARM 指令从一个内存位置复制到另一个内存位置, + 并注意相应地调整位置相关指令。 + 源地址由 `inputCode` 指定,这是一个 **[NativePointer](#nativepointer)**。 + 目的地由 `output` 给出,这是一个指向所需目标内存地址的 **[ThumbWriter](#thumbwriter)**。 + +- `reset(inputCode, output)`: 回收实例 + +- `dispose()`: 立即清理内存 + +- `input`: 到目前为止读取的最新 **[Instruction](#instruction)**。开始时为 `null`, + 并在每次调用 [`readOne()`](#thumbrelocator-readone) 时更改。 + +- `eob`: 布尔值,指示是否已到达块的末尾,即我们已到达任何类型的分支,如 CALL、JMP、BL、RET。 + +- `eoi`: 布尔值,指示是否已到达输入的末尾,例如我们已到达 JMP/B/RET,这是一条指令,其后可能有也可能没有有效代码。 + +- `readOne()`: 将下一条指令读入重定位器的内部缓冲区,并返回到目前为止读取的字节数,包括以前的调用。 + 你可以继续调用此方法以继续缓冲,或者立即调用 [`writeOne()`](#thumbrelocator-writeone) 或 [`skipOne()`](#thumbrelocator-skipone)。 + 或者,你可以缓冲直到所需的点,然后调用 [`writeAll()`](#thumbrelocator-writeall)。 + 当到达输入末尾时返回零,这意味着 `eoi` 属性现在为 `true`。 + {: #thumbrelocator-readone} + +- `peekNextWriteInsn()`: 查看要写入或跳过的下一条 **[Instruction](#instruction)** + +- `peekNextWriteSource()`: 查看要写入或跳过的下一条指令的地址 + +- `skipOne()`: 跳过本来要写入的下一条指令 + {: #thumbrelocator-skipone} + +- `writeOne()`: 写入下一条缓冲的指令 + {: #thumbrelocator-writeone} + +- `copyOne()`: 复制下一条缓冲的指令而不推进输出光标,允许将同一条指令写出多次 + +- `writeAll()`: 写入所有缓冲的指令 + {: #thumbrelocator-writeall} + + +### ARM enum types + +- Register: `r0` `r1` `r2` `r3` `r4` `r5` `r6` `r7` `r8` `r9` `r10` `r11` + `r12` `r13` `r14` `r15` `sp` `lr` `sb` `sl` `fp` `ip` `pc` `s0` `s1` `s2` + `s3` `s4` `s5` `s6` `s7` `s8` `s9` `s10` `s11` `s12` `s13` `s14` `s15` + `s16` `s17` `s18` `s19` `s20` `s21` `s22` `s23` `s24` `s25` `s26` `s27` + `s28` `s29` `s30` `s31` `d0` `d1` `d2` `d3` `d4` `d5` `d6` `d7` `d8` `d9` + `d10` `d11` `d12` `d13` `d14` `d15` `d16` `d17` `d18` `d19` `d20` `d21` + `d22` `d23` `d24` `d25` `d26` `d27` `d28` `d29` `d30` `d31` `q0` `q1` `q2` + `q3` `q4` `q5` `q6` `q7` `q8` `q9` `q10` `q11` `q12` `q13` `q14` `q15` +- SystemRegister: `apsr-nzcvq` +- ConditionCode: `eq` `ne` `hs` `lo` `mi` `pl` `vs` `vc` `hi` `ls` `ge` `lt` + `gt` `le` `al` +- Shifter: `asr` `lsl` `lsr` `ror` `rrx` `asr-reg` `lsl-reg` `lsr-reg` + `ror-reg` `rrx-reg` + + +### Arm64Writer + ++ `new Arm64Writer(codeAddress[, { pc: ptr('0x1234') }])`: 创建一个新的代码编写器, + 用于生成直接写入 `codeAddress` 处内存的 AArch64 机器代码,`codeAddress` 指定为 **[NativePointer](#nativepointer)**。 + 第二个参数是一个可选的选项对象,可以在其中指定初始程序计数器,这在生成代码到暂存缓冲区时很有用。 + 这在 iOS 上使用 [`Memory.patchCode()`](#memory-patchcode) 时至关重要,因为它可能会为你提供一个临时位置, + 稍后会将其映射到预期内存位置的内存中。 + +- `reset(codeAddress[, { pc: ptr('0x1234') }])`: 回收实例 + +- `dispose()`: 立即清理内存 + +- `flush()`: 解析标签引用并将挂起的数据写入内存。完成代码生成后,你应该始终调用一次。 + 通常也希望在不相关的代码片段之间执行此操作,例如一次生成多个函数时。 + +- `base`: 输出的第一个字节的内存位置,作为 NativePointer + +- `code`: 输出的下一个字节的内存位置,作为 NativePointer + +- `pc`: 输出的下一个字节处的程序计数器,作为 NativePointer + +- `offset`: 当前偏移量作为 JavaScript 数字 + +- `skip(nBytes)`: 跳过 `nBytes` + +- `putLabel(id)`: 在当前位置放置一个标签,其中 `id` 是一个可以在过去和将来的 `put*Label()` 调用中引用的字符串 + {: #arm64writer-putlabel} + +- `putCallAddressWithArguments(func, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码, + `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或 + **[NativePointer](#nativepointer)**。 + +- `putCallRegWithArguments(reg, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码, + `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或 + **[NativePointer](#nativepointer)**。 + +- `putBranchAddress(address)`: 放置分支/跳转到给定地址所需的代码 + +- `canBranchDirectlyBetween(from, to)`: 确定两个给定内存位置之间是否可以直接分支 + +- `putBImm(address)`: 放置一个 B 指令 + +- `putBLabel(labelId)`: 放置一个引用 `labelId` 的 B 指令, + `labelId` 由过去或将来的 [`putLabel()`](#arm64writer-putlabel) 定义 + +- `putBCondLabel(cc, labelId)`: 放置一个引用 `labelId` 的 B COND 指令, + `labelId` 由过去或将来的 [`putLabel()`](#arm64writer-putlabel) 定义 + +- `putBlImm(address)`: 放置一个 BL 指令 + +- `putBlLabel(labelId)`: 放置一个引用 `labelId` 的 BL 指令, + `labelId` 由过去或将来的 [`putLabel()`](#arm64writer-putlabel) 定义 + +- `putBrReg(reg)`: 放置一个 BR 指令 + +- `putBrRegNoAuth(reg)`: 放置一个 BR 指令,期望一个没有任何认证位的原始指针 + +- `putBlrReg(reg)`: 放置一个 BLR 指令 + +- `putBlrRegNoAuth(reg)`: 放置一个 BLR 指令,期望一个没有任何认证位的原始指针 + +- `putRet()`: 放置一个 RET 指令 + +- `putRetReg(reg)`: 放置一个 RET 指令 + +- `putCbzRegImm(reg, target)`: 放置一个 CBZ 指令 + +- `putCbnzRegImm(reg, target)`: 放置一个 CBNZ 指令 + +- `putCbzRegLabel(reg, labelId)`: 放置一个引用 `labelId` 的 CBZ 指令, + `labelId` 由过去或将来的 [`putLabel()`](#arm64writer-putlabel) 定义 + +- `putCbnzRegLabel(reg, labelId)`: 放置一个引用 `labelId` 的 CBNZ 指令, + `labelId` 由过去或将来的 [`putLabel()`](#arm64writer-putlabel) 定义 + +- `putTbzRegImmImm(reg, bit, target)`: 放置一个 TBZ 指令 + +- `putTbnzRegImmImm(reg, bit, target)`: 放置一个 TBNZ 指令 + +- `putTbzRegImmLabel(reg, bit, labelId)`: 放置一个引用 `labelId` 的 TBZ 指令, + `labelId` 由过去或将来的 [`putLabel()`](#arm64writer-putlabel) 定义 + +- `putTbnzRegImmLabel(reg, bit, labelId)`: 放置一个引用 `labelId` 的 TBNZ 指令, + `labelId` 由过去或将来的 [`putLabel()`](#arm64writer-putlabel) 定义 + +- `putPushRegReg(regA, regB)`: 放置一个 PUSH 指令 + +- `putPopRegReg(regA, regB)`: 放置一个 POP 指令 + +- `putPushAllXRegisters()`: 放置将所有 X 寄存器推入堆栈所需的代码 + +- `putPopAllXRegisters()`: 放置将所有 X 寄存器从堆栈弹出所需的代码 + +- `putPushAllQRegisters()`: 放置将所有 Q 寄存器推入堆栈所需的代码 + +- `putPopAllQRegisters()`: 放置将所有 Q 寄存器从堆栈弹出所需的代码 + +- `putLdrRegAddress(reg, address)`: 放置一个 LDR 指令 + +- `putLdrRegU32(reg, val)`: 放置一个 LDR 指令 + +- `putLdrRegU64(reg, val)`: 放置一个 LDR 指令 + +- `putLdrRegU32Ptr(reg, srcAddress)`: 放置一个 LDR 指令 + +- `putLdrRegU64Ptr(reg, srcAddress)`: 放置一个 LDR 指令 + +- `putLdrRegRef(reg)`: 放置一个带有悬空数据引用的 LDR 指令, + 返回一个不透明的 ref 值,该值应传递给所需位置的 [`putLdrRegValue()`](#arm64writer-putldrregvalue) + {: #arm64writer-putldrregref} + +- `putLdrRegValue(ref, value)`: 从先前的 [`putLdrRegRef()`](#arm64writer-putldrregref) 放置值并更新 LDR 指令 + {: #arm64writer-putldrregvalue} + +- `putLdrRegReg(dstReg, srcReg)`: 放置一个 LDR 指令 + +- `putLdrRegRegOffset(dstReg, srcReg, srcOffset)`: 放置一个 LDR 指令 + +- `putLdrRegRegOffsetMode(dstReg, srcReg, srcOffset, mode)`: 放置一个 LDR MODE 指令 + +- `putLdrswRegRegOffset(dstReg, srcReg, srcOffset)`: 放置一个 LDRSW 指令 + +- `putAdrpRegAddress(reg, address)`: 放置一个 ADRP 指令 + +- `putStrRegReg(srcReg, dstReg)`: 放置一个 STR 指令 + +- `putStrRegRegOffset(srcReg, dstReg, dstOffset)`: 放置一个 STR 指令 + +- `putStrRegRegOffsetMode(srcReg, dstReg, dstOffset, mode)`: 放置一个 STR MODE 指令 + +- `putLdpRegRegRegOffset(regA, regB, regSrc, srcOffset, mode)`: 放置一个 LDP 指令 + +- `putStpRegRegRegOffset(regA, regB, regDst, dstOffset, mode)`: 放置一个 STP 指令 + +- `putMovRegReg(dstReg, srcReg)`: 放置一个 MOV 指令 + +- `putMovRegNzcv(reg)`: 放置一个 MOV NZCV 指令 + +- `putMovNzcvReg(reg)`: 放置一个 MOV NZCV 指令 + +- `putUxtwRegReg(dstReg, srcReg)`: 放置一个 UXTW 指令 + +- `putAddRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 ADD 指令 + +- `putAddRegRegReg(dstReg, leftReg, rightReg)`: 放置一个 ADD 指令 + +- `putSubRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 SUB 指令 + +- `putSubRegRegReg(dstReg, leftReg, rightReg)`: 放置一个 SUB 指令 + +- `putAndRegRegImm(dstReg, leftReg, rightValue)`: 放置一个 AND 指令 + +- `putEorRegRegReg(dstReg, leftReg, rightReg)`: 放置一个 EOR 指令 + +- `putUbfm(dstReg, srcReg, imms, immr)`: 放置一个 UBFM 指令 + +- `putLslRegImm(dstReg, srcReg, shift)`: 放置一个 LSL 指令 + +- `putLsrRegImm(dstReg, srcReg, shift)`: 放置一个 LSR 指令 + +- `putTstRegImm(reg, immValue)`: 放置一个 TST 指令 + +- `putCmpRegReg(regA, regB)`: 放置一个 CMP 指令 + +- `putXpaciReg(reg)`: 放置一个 XPACI 指令 + +- `putNop()`: 放置一个 NOP 指令 + +- `putBrkImm(imm)`: 放置一个 BRK 指令 + +- `putMrs(dstReg, systemReg)`: 放置一个 MRS 指令 + +- `putInstruction(insn)`: 将原始指令作为 JavaScript 数字放置 + +- `putBytes(data)`: 放置来自提供的 **[ArrayBuffer](#arraybuffer)** 的原始数据 + +- `sign(value)`: 对给定的指针值进行签名 + + +### Arm64Relocator + ++ `new Arm64Relocator(inputCode, output)`: 创建一个新的代码重定位器,用于将 AArch64 指令从一个内存位置复制到另一个内存位置, + 并注意相应地调整位置相关指令。 + 源地址由 `inputCode` 指定,这是一个 **[NativePointer](#nativepointer)**。 + 目的地由 `output` 给出,这是一个指向所需目标内存地址的 **[Arm64Writer](#arm64writer)**。 + +- `reset(inputCode, output)`: 回收实例 + +- `dispose()`: 立即清理内存 + +- `input`: 到目前为止读取的最新 **[Instruction](#instruction)**。开始时为 `null`, + 并在每次调用 [`readOne()`](#arm64relocator-readone) 时更改。 + +- `eob`: 布尔值,指示是否已到达块的末尾,即我们已到达任何类型的分支,如 CALL、JMP、BL、RET。 + +- `eoi`: 布尔值,指示是否已到达输入的末尾,例如我们已到达 JMP/B/RET,这是一条指令,其后可能有也可能没有有效代码。 + +- `readOne()`: 将下一条指令读入重定位器的内部缓冲区,并返回到目前为止读取的字节数,包括以前的调用。 + 你可以继续调用此方法以继续缓冲,或者立即调用 [`writeOne()`](#arm64relocator-writeone) 或 [`skipOne()`](#arm64relocator-skipone)。 + 或者,你可以缓冲直到所需的点,然后调用 [`writeAll()`](#arm64relocator-writeall)。 + 当到达输入末尾时返回零,这意味着 `eoi` 属性现在为 `true`。 + {: #arm64relocator-readone} + +- `peekNextWriteInsn()`: 查看要写入或跳过的下一条 **[Instruction](#instruction)** + +- `peekNextWriteSource()`: 查看要写入或跳过的下一条指令的地址 + +- `skipOne()`: 跳过本来要写入的下一条指令 + {: #arm64relocator-skipone} + +- `writeOne()`: 写入下一条缓冲的指令 + {: #arm64relocator-writeone} + +- `writeAll()`: 写入所有缓冲的指令 + {: #arm64relocator-writeall} + + +### AArch64 enum types + +- Register: `x0` `x1` `x2` `x3` `x4` `x5` `x6` `x7` `x8` `x9` `x10` `x11` + `x12` `x13` `x14` `x15` `x16` `x17` `x18` `x19` `x20` `x21` `x22` `x23` + `x24` `x25` `x26` `x27` `x28` `x29` `x30` `w0` `w1` `w2` `w3` `w4` `w5` + `w6` `w7` `w8` `w9` `w10` `w11` `w12` `w13` `w14` `w15` `w16` `w17` `w18` + `w19` `w20` `w21` `w22` `w23` `w24` `w25` `w26` `w27` `w28` `w29` `w30` + `sp` `lr` `fp` `wsp` `wzr` `xzr` `nzcv` `ip0` `ip1` `s0` `s1` `s2` `s3` + `s4` `s5` `s6` `s7` `s8` `s9` `s10` `s11` `s12` `s13` `s14` `s15` `s16` + `s17` `s18` `s19` `s20` `s21` `s22` `s23` `s24` `s25` `s26` `s27` `s28` + `s29` `s30` `s31` `d0` `d1` `d2` `d3` `d4` `d5` `d6` `d7` `d8` `d9` `d10` + `d11` `d12` `d13` `d14` `d15` `d16` `d17` `d18` `d19` `d20` `d21` `d22` + `d23` `d24` `d25` `d26` `d27` `d28` `d29` `d30` `d31` `q0` `q1` `q2` `q3` + `q4` `q5` `q6` `q7` `q8` `q9` `q10` `q11` `q12` `q13` `q14` `q15` `q16` + `q17` `q18` `q19` `q20` `q21` `q22` `q23` `q24` `q25` `q26` `q27` `q28` + `q29` `q30` `q31` +- ConditionCode: `eq` `ne` `hs` `lo` `mi` `pl` `vs` `vc` `hi` `ls` `ge` `lt` + `gt` `le` `al` `nv` +- IndexMode: `post-adjust` `signed-offset` `pre-adjust` + + +### MipsWriter + ++ `new MipsWriter(codeAddress[, { pc: ptr('0x1234') }])`: 创建一个新的代码编写器, + 用于生成直接写入 `codeAddress` 处内存的 MIPS 机器代码,`codeAddress` 指定为 **[NativePointer](#nativepointer)**。 + 第二个参数是一个可选的选项对象,可以在其中指定初始程序计数器,这在生成代码到暂存缓冲区时很有用。 + 这在 iOS 上使用 [`Memory.patchCode()`](#memory-patchcode) 时至关重要,因为它可能会为你提供一个临时位置, + 稍后会将其映射到预期内存位置的内存中。 + +- `reset(codeAddress[, { pc: ptr('0x1234') }])`: 回收实例 + +- `dispose()`: 立即清理内存 + +- `flush()`: 解析标签引用并将挂起的数据写入内存。完成代码生成后,你应该始终调用一次。 + 通常也希望在不相关的代码片段之间执行此操作,例如一次生成多个函数时。 + +- `base`: 输出的第一个字节的内存位置,作为 **[NativePointer](#nativepointer)** + +- `code`: 输出的下一个字节的内存位置,作为 **[NativePointer](#nativepointer)** + +- `pc`: 输出的下一个字节处的程序计数器,作为 **[NativePointer](#nativepointer)** + +- `offset`: 当前偏移量作为 JavaScript 数字 + +- `skip(nBytes)`: 跳过 `nBytes` + +- `putLabel(id)`: 在当前位置放置一个标签,其中 `id` 是一个可以在过去和将来的 `put*Label()` 调用中引用的字符串 + {: #mipswriter-putlabel} + +- `putCallAddressWithArguments(func, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码, + `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或 + **[NativePointer](#nativepointer)**。 + +- `putCallRegWithArguments(reg, args)`: 放置调用具有指定 `args` 的 C 函数所需的代码, + `args` 指定为 JavaScript 数组,其中每个元素要么是指定寄存器的字符串,要么是指定立即值的数字或 + **[NativePointer](#nativepointer)**。 + +- `putJAddress(address)`: 放置一个 J 指令 + +- `putJAddressWithoutNop(address)`: 放置一个 J WITHOUT NOP 指令 + +- `putJLabel(labelId)`: 放置一个引用 `labelId` 的 J 指令, + `labelId` 由过去或将来的 [`putLabel()`](#mipswriter-putlabel) 定义 + +- `putJrReg(reg)`: 放置一个 JR 指令 + +- `putJalAddress(address)`: 放置一个 JAL 指令 + +- `putJalrReg(reg)`: 放置一个 JALR 指令 + +- `putBOffset(offset)`: 放置一个 B 指令 + +- `putBeqRegRegLabel(rightReg, leftReg, labelId)`: 放置一个引用 `labelId` 的 BEQ 指令, + `labelId` 由过去或将来的 [`putLabel()`](#mipswriter-putlabel) 定义 + +- `putRet()`: 放置一个 RET 指令 + +- `putLaRegAddress(reg, address)`: 放置一个 LA 指令 + +- `putLuiRegImm(reg, imm)`: 放置一个 LUI 指令 + +- `putDsllRegReg(dstReg, srcReg, amount)`: 放置一个 DSLL 指令 + +- `putOriRegRegImm(rt, rs, imm)`: 放置一个 ORI 指令 + +- `putLdRegRegOffset(dstReg, srcReg, srcOffset)`: 放置一个 LD 指令 + +- `putLwRegRegOffset(dstReg, srcReg, srcOffset)`: 放置一个 LW 指令 + +- `putSwRegRegOffset(srcReg, dstReg, dstOffset)`: 放置一个 SW 指令 + +- `putMoveRegReg(dstReg, srcReg)`: 放置一个 MOVE 指令 + +- `putAdduRegRegReg(dstReg, leftReg, rightReg)`: 放置一个 ADDU 指令 + +- `putAddiRegRegImm(dstReg, leftReg, imm)`: 放置一个 ADDI 指令 + +- `putAddiRegImm(dstReg, imm)`: 放置一个 ADDI 指令 + +- `putSubRegRegImm(dstReg, leftReg, imm)`: 放置一个 SUB 指令 + +- `putPushReg(reg)`: 放置一个 PUSH 指令 + +- `putPopReg(reg)`: 放置一个 POP 指令 + +- `putMfhiReg(reg)`: 放置一个 MFHI 指令 + +- `putMfloReg(reg)`: 放置一个 MFLO 指令 + +- `putMthiReg(reg)`: 放置一个 MTHI 指令 + +- `putMtloReg(reg)`: 放置一个 MTLO 指令 + +- `putNop()`: 放置一个 NOP 指令 + +- `putBreak()`: 放置一个 BREAK 指令 + +- `putPrologueTrampoline(reg, address)`: 放置一个最小尺寸的 trampoline 用于跳转到给定地址 + +- `putInstruction(insn)`: 将原始指令作为 JavaScript 数字放置 + +- `putBytes(data)`: 放置来自提供的 **[ArrayBuffer](#arraybuffer)** 的原始数据 + + +### MipsRelocator + ++ `new MipsRelocator(inputCode, output)`: 创建一个新的代码重定位器,用于将 MIPS 指令从一个内存位置复制到另一个内存位置, + 并注意相应地调整位置相关指令。 + 源地址由 `inputCode` 指定,这是一个 **[NativePointer](#nativepointer)**。 + 目的地由 `output` 给出,这是一个指向所需目标内存地址的 **[MipsWriter](#mipswriter)**。 + +- `reset(inputCode, output)`: 回收实例 + +- `dispose()`: 立即清理内存 + +- `input`: 到目前为止读取的最新 **[Instruction](#instruction)**。开始时为 `null`, + 并在每次调用 [`readOne()`](#mipsrelocator-readone) 时更改。 + +- `eob`: 布尔值,指示是否已到达块的末尾,即我们已到达任何类型的分支,如 CALL、JMP、BL、RET。 + +- `eoi`: 布尔值,指示是否已到达输入的末尾,例如我们已到达 JMP/B/RET,这是一条指令,其后可能有也可能没有有效代码。 + +- `readOne()`: 将下一条指令读入重定位器的内部缓冲区,并返回到目前为止读取的字节数,包括以前的调用。 + 你可以继续调用此方法以继续缓冲,或者立即调用 [`writeOne()`](#mipsrelocator-writeone) 或 [`skipOne()`](#mipsrelocator-skipone)。 + 或者,你可以缓冲直到所需的点,然后调用 [`writeAll()`](#mipsrelocator-writeall)。 + 当到达输入末尾时返回零,这意味着 `eoi` 属性现在为 `true`。 + {: #mipsrelocator-readone} + +- `peekNextWriteInsn()`: 查看要写入或跳过的下一条 **[Instruction](#instruction)** + +- `peekNextWriteSource()`: 查看要写入或跳过的下一条指令的地址 + +- `skipOne()`: 跳过本来要写入的下一条指令 + {: #mipsrelocator-skipone} + +- `writeOne()`: 写入下一条缓冲的指令 + {: #mipsrelocator-writeone} + +- `writeAll()`: 写入所有缓冲的指令 + {: #mipsrelocator-writeall} + + +### MIPS enum types + +- Register: `v0` `v1` `a0` `a1` `a2` `a3` `t0` `t1` `t2` `t3` `t4` `t5` `t6` + `t7` `s0` `s1` `s2` `s3` `s4` `s5` `s6` `s7` `t8` `t9` `k0` `k1` `gp` `sp` + `fp` `s8` `ra` `hi` `lo` `zero` `at` `0` `1` `2` `3` `4` `5` `6` `7` `8` + `9` `10` `11` `12` `13` `14` `15` `16` `17` `18` `19` `20` `21` `22` `23` + `24` `25` `26` `27` `28` `29` `30` `31` + +--- + +## 其他 + + +### Console + ++ `console.log(line)`, `console.warn(line)`, `console.error(line)`: + 将 `line` 写入基于 Frida 的应用程序的控制台。确切的行为取决于 [frida-core](https://github.com/frida/frida-core) + 集成的位置。 + 例如,当通过 [frida-python](https://github.com/frida/frida-python) 使用 Frida 时, + 此输出将转到 *stdout* 或 *stderr*,当使用 **[frida-qml](https://github.com/frida/frida-qml)** 时, + 转到 **[qDebug](https://doc.qt.io/qt-5/qdebug.html)** 等。 + + 作为 **[ArrayBuffer](#arraybuffer)** 对象的参数将被替换为具有默认选项的 [`hexdump()`](#hexdump) 的结果。 + + +### Hexdump + ++ `hexdump(target[, options])`: 从提供的 **[ArrayBuffer](#arraybuffer)** 或 [NativePointer](#nativepointer) `target` 生成十六进制转储, + 可选地使用 `options` 自定义输出。 + + 例如: + +{% highlight js %} +const libc = Process.getModuleByName('libc.so').base; +console.log(hexdump(libc, { + /* address: ptr('0x1000'), -- to override the base address */ + offset: 0, + length: 64, + header: true, + ansi: true +})); +{% endhighlight %} + +{% highlight sh %} + 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF +00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 .ELF............ +00000010 03 00 28 00 01 00 00 00 00 00 00 00 34 00 00 00 ..(.........4... +00000020 34 a8 04 00 00 00 00 05 34 00 20 00 08 00 28 00 4.......4. ...(. +00000030 1e 00 1d 00 06 00 00 00 34 00 00 00 34 00 00 00 ........4...4... +{% endhighlight %} + + +### Shorthand + ++ `int64(v)`: [`new Int64(v)`](#int64) 的简写 + ++ `uint64(v)`: [`new UInt64(v)`](#uint64) 的简写 + ++ `ptr(s)`: [`new NativePointer(s)`](#nativepointer) 的简写 + ++ `NULL`: `ptr("0")` 的简写 + + +### 主机和注入进程之间的通信 + ++ `recv([type, ]callback)`: 请求在从基于 Frida 的应用程序收到下一条消息时调用 `callback`。 + 可选地,可以指定 `type` 以仅接收 `type` 字段设置为 `type` 的消息。 + {: #communication-recv} + + 消息作为第一个参数传递,如果随消息传递了二进制数据,则第二个参数是 **[ArrayBuffer](#arraybuffer)**,否则为 *null*。 + + 这只会给你一条消息,所以你需要再次调用 `recv()` 来接收下一条消息。 + ++ `send(message[, data])`: 将 JavaScript 对象 `message` 发送到基于 Frida 的应用程序(它必须可序列化为 JSON)。 + 如果你还有一些原始二进制数据想随之发送,例如你使用 [`NativePointer#readByteArray`](#nativepointer-readbytearray) + 转储了一些内存,那么你可以通过可选的 `data` 参数传递它。这要求它要么是 **[ArrayBuffer](#arraybuffer)**, + 要么是 0 到 255 之间的整数数组。 + {: #communication-send} + +
+
性能注意事项
+

+ 虽然 send() 是异步的,但发送单个消息的总开销并未针对高频进行优化, + 因此这意味着 Frida 让你根据需要低延迟还是高吞吐量,自行决定将多个值批处理到单个 send() 调用中。 +

+
+ ++ `rpc.exports`: 空对象,你可以替换或插入该对象以向应用程序公开 RPC 风格的 API。 + 键指定方法名称,值是你的导出函数。此函数可以返回一个普通值以立即将其返回给调用者, + 或者返回一个 Promise 以异步返回。 + {: #rpc-exports} + +> 例如: + +{% highlight js %} +rpc.exports = { + add(a, b) { + return a + b; + }, + sub(a, b) { + return new Promise(resolve => { + setTimeout(() => { + resolve(a - b); + }, 100); + }); + } +}; +{% endhighlight %} + +> 从使用 Node.js 绑定的应用程序来看,此 API 将像这样使用: + +{% highlight js %} +const frida = require('frida'); +const fs = require('fs'); +const path = require('path'); +const util = require('util'); + +const readFile = util.promisify(fs.readFile); + +let session, script; +async function run() { + const source = await readFile(path.join(__dirname, '_agent.js'), 'utf8'); + session = await frida.attach('iTunes'); + script = await session.createScript(source); + script.message.connect(onMessage); + await script.load(); + console.log(await script.exports.add(2, 3)); + console.log(await script.exports.sub(5, 3)); +} + +run().catch(onError); + +function onError(error) { + console.error(error.stack); +} + +function onMessage(message, data) { + if (message.type === 'send') { + console.log(message.payload); + } else if (message.type === 'error') { + console.error(message.stack); + } +} +{% endhighlight %} + +> Python 版本将非常相似: + +{% highlight py %} +import codecs +import frida + +def on_message(message, data): + if message['type'] == 'send': + print(message['payload']) + elif message['type'] == 'error': + print(message['stack']) + +session = frida.attach('iTunes') +with codecs.open('./agent.js', 'r', 'utf-8') as f: + source = f.read() +script = session.create_script(source) +script.on('message', on_message) +script.load() +print(script.exports.add(2, 3)) +print(script.exports.sub(5, 3)) +session.detach() +{% endhighlight %} + +在上面的示例中,我们使用 `script.on('message', on_message)` 来监视来自注入进程(JavaScript 端)的任何消息。 +你也可以在 `script` 和 `session` 上监视其他通知。 +如果你想在目标进程退出时收到通知,请使用 `session.on('detached', your_function)`。 + + +### 定时事件 + ++ `setTimeout(func, delay[, ...parameters])`: 在 `delay` 毫秒后调用 `func`, + 可选地传递一个或多个 `parameters`。 + 返回一个 id,可以传递给 `clearTimeout` 以取消它。 + ++ `clearTimeout(id)`: 取消由调用 `setTimeout` 返回的 id。 + ++ `setInterval(func, delay[, ...parameters])`: 每 `delay` 毫秒调用 `func`, + 可选地传递一个或多个 `parameters`。 + 返回一个 id,可以传递给 `clearInterval` 以取消它。 + ++ `clearInterval(id)`: 取消由调用 `setInterval` 返回的 id。 + ++ `setImmediate(func[, ...parameters])`: 安排在 Frida 的 JavaScript 线程上尽快调用 `func`, + 可选地传递一个或多个 `parameters`。 + 返回一个 id,可以传递给 `clearImmediate` 以取消它。 + ++ `clearImmediate(id)`: 取消由调用 `setImmediate` 返回的 id。 + + +### 垃圾回收 + ++ `gc()`: 强制垃圾回收。对于测试很有用,尤其是涉及 [`Script.bindWeak()`](#bindweak) 的逻辑。 + + +### Worker + +具有自己的 JavaScript 堆、锁等的 Worker 脚本。 + +这对于将繁重的处理移动到后台线程很有用,从而允许及时处理钩子。 + ++ `new Worker(url[, options])`: 创建一个新的 worker,执行指定 `url` 处的脚本。 + + URL 通常通过让模块导出其 `import.meta.url` 并从创建 worker 的模块导入该 URL 来检索。 + + 如果指定,`options` 是一个可能包含以下一个或多个键的对象: + + - `onMessage`: 当 worker 使用 **[send()](#communication-send)** 发出消息时调用的函数。 + 回调签名与 **[recv()](#communication-recv)** 相同。 + +- `terminate()`: 终止 worker。 + +- `post(message[, data])`: 向 worker 发布消息。签名与 **[send()](#communication-send)** 相同。 + 在 worker 内部使用 **[recv()](#communication-recv)** 接收它。 + +- `exports`: 用于调用 worker 定义的 **[rpc.exports](#rpc-exports)** 的魔术代理对象。 + 每个函数返回一个 *Promise*,你可以在 *async* 函数内部 *await* 它。 + + +### Cloak + +让你在进程内省期间看不到自己。 + +诸如 [`Process.enumerateThreads()`](#process-enumeratethreads) 之类的内省 API 确保跳过隐形资源, +并且事情看起来就像你不在被检测的进程中一样。 + +Frida 运行时创建的任何资源都将自动隐形。这意味着你通常只需要在使用特定于操作系统的 API 创建给定资源时管理隐形资源。 + ++ `Cloak.addThread(id)`: 更新隐形资源注册表,使给定的线程 `id` 对隐形感知 API 不可见, + 例如 [`Process.enumerateThreads()`](#process-enumeratethreads)。 + ++ `Cloak.removeThread(id)`: 更新隐形资源注册表,使给定的线程 `id` 对隐形感知 API 可见, + 例如 [`Process.enumerateThreads()`](#process-enumeratethreads)。 + ++ `Cloak.hasCurrentThread()`: 返回一个布尔值,指示当前线程当前是否被隐形。 + ++ `Cloak.hasThread(id)`: 返回一个布尔值,指示给定的线程 `id` 当前是否被隐形。 + ++ `Cloak.addRange(range)`: 更新隐形资源注册表,使给定的内存 `range` 对隐形感知 API 不可见, + 例如 [`Process.enumerateRanges()`](#process-enumerateranges)。 + 提供的 `range` 是一个具有 `base` 和 `size` 属性的对象——就像例如 + [`Process.getModuleByName()`](#process-getmodulebyname) 返回的对象中的属性一样。 + {: #cloak-addrange} + ++ `Cloak.removeRange(range)`: 更新隐形资源注册表,使给定的内存 `range` 对隐形感知 API 可见, + 例如 [`Process.enumerateRanges()`](#process-enumerateranges)。 + 提供的 `range` 是一个具有 `base` 和 `size` 属性的对象——就像例如 + [`Process.getModuleByName()`](#process-getmodulebyname) 返回的对象中的属性一样。 + ++ `Cloak.hasRangeContaining(address)`: 返回一个布尔值,指示包含 `address` 的内存范围当前是否被隐形, + 指定为 [NativePointer](#nativepointer)。 + ++ `Cloak.clipRange(range)`: 确定给定的内存 `range` 当前有多少是可见的。 + 提供的 `range` 是一个具有 `base` 和 `size` 属性的对象——就像例如 + [`Process.getModuleByName()`](#process-getmodulebyname) 返回的对象中的属性一样。 + 返回此类对象的数组,指示 `range` 的可见部分。如果整个范围被隐形,将返回一个空数组, + 如果它完全可见,则返回 `null`。 + ++ `Cloak.addFileDescriptor(fd)`: 更新隐形资源注册表,使给定的文件描述符 `fd` 对隐形感知 API 不可见。 + ++ `Cloak.removeFileDescriptor(fd)`: 更新隐形资源注册表,使给定的文件描述符 `fd` 对隐形感知 API 可见。 + ++ `Cloak.hasFileDescriptor(fd)`: 返回一个布尔值,指示给定的文件描述符 `fd` 当前是否被隐形。 + + +### Profiler + +建立在 `Interceptor` 之上的简单最坏情况分析器。 + +与以特定频率对调用堆栈进行采样的传统分析器不同,你决定你感兴趣的分析的确切函数。 + +当任何这些函数被调用时,分析器会在进入时获取一个样本,并在返回时获取另一个样本。 +然后它减去这两个样本,以计算调用的昂贵程度。如果结果值大于它之前为特定函数看到的值, +则该值将成为其新的最坏情况。 + +每当发现新的最坏情况时,知道大部分时间/周期/等都花在特定函数上并不一定足够。 +例如,该函数可能仅在某些输入参数下很慢。 + +这是你可以为特定函数传递 [`describe`](#profiler-describe) 回调的情况。 +你的回调应该从参数列表和/或其他相关状态捕获相关上下文,并返回一个描述刚刚发现的新最坏情况的字符串。 + +当你稍后决定调用 `generateReport()` 时,你会发现你计算的描述嵌入在每个最坏情况条目中。 + ++ `new Profiler()`: 创建一个 Profiler。 + +- `instrument(functionAddress, sampler[, callbacks])`: 开始使用 [`sampler`](#sampler) + 检测由 `functionAddress` [`NativePointer`](#nativepointer) 指定的指定函数。 + + 可选的 `callbacks` 参数是一个可能包含以下内容的对象: + + - `describe(args)`: 当发现新的最坏情况并且应该从参数列表和/或其他相关状态捕获描述时同步调用。 + 实现必须返回描述参数列表的字符串。有关 `args` 以及 `this` 如何绑定的更多详细信息, + 请参阅 Interceptor [`onEnter`](#interceptor-onenter)。 + {: #profiler-describe} + +- `generateReport()`: 从实时分析器状态生成 XML 报告,作为字符串返回。可以在任何时候调用,次数不限。 + + +### Sampler + +- `sample()`: 检索一个新样本,作为 bigint 返回。它表示什么取决于特定的采样器。 + + +### CycleSampler + +测量 CPU 周期的采样器,例如在 x86 上使用 RDTSC 指令。 + ++ `new CycleSampler()`: 创建一个 CycleSampler。 + +### BusyCycleSampler + +仅测量当前线程花费的 CPU 周期的采样器,例如在 Windows 上使用 +QueryThreadCycleTime()。 + ++ `new BusyCycleSampler()`: 创建一个 BusyCycleSampler。 + + +### WallClockSampler + +测量时间流逝的采样器。 + ++ `new WallClockSampler()`: 创建一个 WallClockSampler。 + + +### UserTimeSampler + +测量在用户空间花费的时间的采样器。 + ++ `new UserTimeSampler([threadId])`: 创建一个 UserTimeSampler,对 `threadId` 指定的线程进行采样(作为数字), + 如果省略,则对当前线程进行采样。 + + +### MallocCountSampler + +计算 malloc()、calloc() 和 realloc() 调用次数的采样器。 + ++ `new MallocCountSampler()`: 创建一个 MallocCountSampler。 + + +### CallCountSampler + +计算对你选择的函数的调用次数的采样器。 + ++ `new CallCountSampler(functions)`: 创建一个 CallCountSampler, + 对 `functions` 的调用次数进行采样,`functions` 是一个 [`NativePointer`](#nativepointer) 值数组, + 指定要计算调用次数的函数。 + + +[r2]: https://radare.org/r/ diff --git a/_i18n/cn/_docs/messages.md b/_i18n/cn/_docs/messages.md new file mode 100644 index 00000000..297e645e --- /dev/null +++ b/_i18n/cn/_docs/messages.md @@ -0,0 +1,183 @@ +在本教程中,我们将展示如何在目标进程之间发送和接收消息。 + +## 设置实验 + +创建一个文件 `hello.c`: + +{% highlight c %} +#include +#include + +void +f (int n) +{ + printf ("Number: %d\n", n); +} + +int +main (int argc, + char * argv[]) +{ + int i = 0; + + printf ("f() is at %p\n", f); + + while (1) + { + f (i++); + sleep (1); + } +} +{% endhighlight %} + +使用以下命令编译: + +{% highlight bash %} +$ gcc -Wall hello.c -o hello +{% endhighlight %} + +启动程序并记下 `f()` 的地址(在以下示例中为 `0x400544`): + +{% highlight bash %} +f() is at 0x400544 +Number: 0 +Number: 1 +Number: 2 +… +{% endhighlight %} + +## 从目标进程发送消息 + +以下脚本展示了如何将消息发送回 Python 进程。您可以发送任何可序列化为 JSON 的 JavaScript 值。 + +创建一个包含以下内容的 `send.py` 文件: + +{% highlight py %} +import frida +import sys + +session = frida.attach("hello") +script = session.create_script("send(1337);") +def on_message(message, data): + print(message) +script.on('message', on_message) +script.load() +sys.stdin.read() +{% endhighlight %} + +当您运行此脚本时: + +{% highlight bash %} +$ python send.py +{% endhighlight %} + +它应该打印以下消息: + +{% highlight py %} +{'type': 'send', 'payload': 1337} +{% endhighlight %} + +这意味着 JavaScript 代码 `send(1337)` 已在 `hello` 进程内执行。使用 `Ctrl-D` 终止脚本。 + +### 处理来自 JavaScript 的运行时错误 + +如果 JavaScript 脚本抛出未捕获的异常,这将被传播从目标进程到 Python 脚本。如果您将 `send(1337)` 替换为 `send(a)`(一个未定义的变量),Python 将收到以下消息: + +{% highlight py %} +{'type': 'error', 'description': 'ReferenceError: a is not defined', 'lineNumber': 1} +{% endhighlight %} + +注意 `type` 字段(`error` 与 `send`)。 + +## 在目标进程中接收消息 + +可以将消息从 Python 脚本发送到 JavaScript 脚本。创建文件 `pingpong.py`: + +{% highlight py %} +import frida +import sys + +session = frida.attach("hello") +script = session.create_script(""" + recv('poke', function onMessage(pokeMessage) { send('pokeBack'); }); +""") +def on_message(message, data): + print(message) +script.on('message', on_message) +script.load() +script.post({"type": "poke"}) +sys.stdin.read() +{% endhighlight %} + +运行脚本: + +{% highlight bash %} +$ python pingpong.py +{% endhighlight %} + +产生输出: + +{% highlight py %} +{'type': 'send', 'payload': 'pokeBack'} +{% endhighlight %} + +
+
recv() 的机制
+

+ recv() 方法本身是异步的(非阻塞)。注册的回调(onMessage)将恰好接收一条消息。要接收下一条消息,必须使用 recv() 重新注册回调。 +

+
+ +### 目标进程中的阻塞接收 + +可以在 JavaScript 脚本内等待消息到达(阻塞接收)。创建脚本 `rpc.py`: + +{% highlight py %} +import frida +import sys + +session = frida.attach("hello") +script = session.create_script(""" +Interceptor.attach(ptr("%s"), { + onEnter(args) { + send(args[0].toString()); + const op = recv('input', value => { + args[0] = ptr(value.payload); + }); + op.wait(); + } +}); +""" % int(sys.argv[1], 16)) +def on_message(message, data): + print(message) + val = int(message['payload'], 16) + script.post({'type': 'input', 'payload': str(val * 2)}) +script.on('message', on_message) +script.load() +sys.stdin.read() +{% endhighlight %} + +程序 `hello` 应该正在运行,并且您应该记下在其开头打印的地址(例如 `0x400544`)。运行: + +{% highlight bash %} +$ python rpc.py 0x400544 +{% endhighlight %} + +然后观察运行 `hello` 的终端中的变化: + +{% highlight bash %} +Number: 3 +Number: 8 +Number: 10 +Number: 12 +Number: 14 +Number: 16 +Number: 18 +Number: 20 +Number: 22 +Number: 24 +Number: 26 +Number: 14 +{% endhighlight %} + +`hello` 程序应该开始写入“加倍”的值,直到您停止 Python 脚本(`Ctrl-D`)。 diff --git a/_i18n/cn/_docs/modes.md b/_i18n/cn/_docs/modes.md new file mode 100644 index 00000000..523db649 --- /dev/null +++ b/_i18n/cn/_docs/modes.md @@ -0,0 +1,25 @@ +Frida 通过其强大的插桩核心 Gum(用 C 编写)提供动态插桩。因为这种插桩逻辑容易发生变化,所以您通常希望用脚本语言编写它,以便在开发和维护它时获得较短的反馈循环。这就是 GumJS 发挥作用的地方。只需几行 C 代码,您就可以在运行时内运行一段 JavaScript,该运行时具有对 Gum API 的完全访问权限,允许您 hook 函数、枚举加载的库、它们的导入和导出函数、读写内存、扫描内存模式等。 + +## 目录 + 1. [注入](#注入) + 1. [嵌入](#嵌入) + 1. [预加载](#预加载) + +## 注入 + +然而,大多数时候,您希望启动一个现有的程序,附加到一个正在运行的程序,或者在它被启动时劫持它,然后在其中运行您的插桩逻辑。由于这是使用 Frida 的一种非常常见的方式,因此这也是我们大部分文档关注的内容。此功能由 frida-core 提供,它充当物流层,将 GumJS 打包到一个共享库中,将其注入到现有软件中,并提供一个双向通信通道,以便在需要时与您的脚本进行对话,并在稍后卸载它们。除了这个核心功能之外,frida-core 还允许您枚举已安装的应用、正在运行的进程和连接的设备。连接的设备通常是运行 *frida-server* 的 iOS 和 Android 设备。该组件本质上只是一个守护进程,它通过 TCP 暴露 frida-core,默认监听 *localhost:27042*。 + +## 嵌入 + +有时无法在[注入](#注入)模式下使用 Frida,例如在未越狱的 iOS 和 Android 系统上。对于这种情况,我们为您提供 *frida-gadget*,这是一个共享库,您应该将其嵌入到要插桩的程序中。只需加载该库,它就允许您使用现有的基于 Frida 的工具(如 [frida-trace][])远程与其交互。它还支持完全自主的方法,即它可以从文件系统运行脚本,而无需任何外部通信。 + +在此处阅读有关 Gadget 的更多信息:[Gadget](/docs/gadget/)。 + +## 预加载 + +也许您熟悉 *LD_PRELOAD* 或 *DYLD_INSERT_LIBRARIES*?如果有 *JS_PRELOAD* 不是很酷吗?这就是上一节中讨论的共享库 *frida-gadget* 在配置为通过从文件系统加载脚本来自主运行时非常有用的地方。 + +在此处阅读有关 Gadget 的更多信息:[Gadget](/docs/gadget/)。 + + +[frida-trace]: /docs/frida-trace/ diff --git a/_i18n/cn/_docs/presentations.md b/_i18n/cn/_docs/presentations.md new file mode 100644 index 00000000..727ab8ca --- /dev/null +++ b/_i18n/cn/_docs/presentations.md @@ -0,0 +1,104 @@ +## Frida 的演讲 + +我们在世界各地的各种会议上介绍了 Frida。随着演示材料的可用,我们将尝试将其放在这里。 + +- [OSDC 2015](https://web.archive.org/web/20160803154827/http://act.osdc.no/osdc2015no/): + [Putting the open back into closed software](https://web.archive.org/web/20160805115358/https://act.osdc.no/osdc2015no/talk/6165) + ([PDF]({{ site.baseurl_root }}/slides/osdc-2015-putting-the-open-back-into-closed-software.pdf) · [Recording](https://youtu.be/tmpjftTHzH8)) + + 有一个你渴望窥视其内部的黑盒进程吗?这个进程也许运行在你的手机上,或者在一个闭源操作系统上,而你必须与它进行互操作?这个专有软件背后的公司是否在 API 和文档方面不太坦诚? + 好吧,如果你懂一点 JavaScript 并且有一点毅力,也许我们可以提供帮助…… + + 在本次演讲中,我们将展示你可以用 Frida 做什么,Frida 是一个适用于 Windows、Mac、Linux、iOS、Android 和 QNX 的可编写脚本的动态二进制插桩工具包。我们通过示例展示了如何用 JavaScript 编写自定义调试代码片段,然后将这些脚本动态插入到正在运行的进程中。Hook 任何函数,监视加密 API 或跟踪私有应用程序代码。无需源代码,无需许可! + +- [OSDC 2015](https://web.archive.org/web/20160803154827/http://act.osdc.no/osdc2015no/): + [The engineering behind the reverse engineering](https://web.archive.org/web/20160413183418/http://act.osdc.no/osdc2015no/talk/6195) + ([PDF]({{ site.baseurl_root }}/slides/osdc-2015-the-engineering-behind-the-reverse-engineering.pdf) · [Recording](https://youtu.be/uc1mbN9EJKQ)) + + 有没有想过如何构建自己的调试器?你十几岁时完成过汇编教程,但从未发现低级编程的任何实际用途?需要学习更多听起来很可怕的技术术语来提高你的薪资等级吗?如果你对以上零个或多个问题的回答是“是”,你可能会对我们提供的内容感兴趣。 + + 在本次演讲中,我们将深入探讨 Frida 背后的工程原理,Frida 是一个多平台可编写脚本的动态二进制插桩工具包。我们解释操作系统进程的基础知识,以及相关的本机操作系统 API。我们展示如何使用这些 API 来探测目标进程的状态(内存、寄存器、线程),以及如何将你自己的代码注入到该进程中。如果时间允许,我们将展示 Frida 如何通过在内存中重写二进制代码来执行其动态插桩,而目标进程正在运行。 + +- [NLUUG 2015](https://www.nluug.nl/activiteiten/events/nj15/index.html): + [Frida: Putting the open back into closed software](https://www.nluug.nl/activiteiten/events/nj15/abstracts/ab08.html) + ([Slides](https://slides.com/oleavr/nluug-2015-frida-putting-the-open-back-into-closed-software) + · [Demos](https://github.com/frida/frida-presentations/tree/master/NLUUG2015) + · [Recording](https://youtu.be/3lo1Y2oKkE4)) + + 有一个你渴望窥视其内部的黑盒进程吗?这个进程也许运行在你的手机上,或者在一个闭源操作系统上,而你必须与它进行互操作?这个专有软件背后的公司是否在 API 和文档方面不太坦诚? + 好吧,如果你懂一点 JavaScript 并且有一点毅力,也许我们可以提供帮助…… + + 在本次演讲中,我们将展示你可以用 Frida 做什么,Frida 是一个适用于 Windows、Mac、Linux、iOS、Android 和 QNX 的可编写脚本的动态二进制插桩工具包。我们通过示例展示了如何用 JavaScript 编写自定义调试代码片段,然后将这些脚本动态插入到正在运行的进程中。Hook 任何函数,监视加密 API 或跟踪私有应用程序代码。无需源代码,无需许可! + +- [ZeroNights 2015](http://2015.zeronights.org/): + [Cross-platform reversing with Frida](http://2015.zeronights.org/workshops.html) + ([PDF]({{ site.baseurl_root }}/slides/zeronights-2015-cross-platform-reversing-with-frida.pdf) + · [Demos](https://github.com/frida/frida-presentations/tree/master/ZeroNights2015)) + + Frida 是一个可编写脚本的动态二进制插桩工具包,旨在大幅缩短动态分析和逆向工程工具的开发周期。它还附带了一些构建在其 API 之上的 CLI 工具。它用可移植的 C 编写,以商业友好的 OSS 许可证发布,具有 Python、Node.js 等语言绑定,是处理所有当前平台(Windows、Mac、Linux、iOS、Android 和 QNX)上二进制文件动态插桩的行业工具。 + + 本次研讨会面向希望快速了解桌面和移动设备上动态插桩最新技术的参与者。我们将从介绍 Frida 的 API 和 CLI 工具开始,然后带你从头开始构建一个逆向工具。 + + 研讨会参与者的要求: + + - 2-3 小时 + - 英语知识 + - 如果你带一台运行 Windows、Mac 或 Linux 的笔记本电脑,以及可选的一台已越狱/root 的 iOS 或 Android 设备,那就太好了 + +- [No cON Name 2015](https://www.noconname.org/): + [Cross-platform reversing with Frida](https://www.noconname.org/) + ([PDF]({{ site.baseurl_root }}/slides/ncn-2015-cross-platform-reversing-with-frida.pdf) + · [Demos](https://github.com/frida/frida-presentations/tree/master/NcN2015)) + + Frida 是一个可编写脚本的动态二进制插桩工具包,旨在大幅缩短动态分析和逆向工程工具的开发周期。它还附带了一些构建在其 API 之上的 CLI 工具。它用可移植的 C 编写,以商业友好的 OSS 许可证发布,具有 Python、Node.js 等语言绑定,是处理所有当前平台(Windows、Mac、Linux、iOS、Android 和 QNX)上二进制文件动态插桩的行业工具。 + + 本次研讨会面向希望快速了解桌面和移动设备上动态插桩最新技术的参与者。我们将从介绍 Frida 的 API 和 CLI 工具开始,然后带你从头开始构建一个逆向工具。 + + 研讨会参与者的要求: + + - 2 小时 + - 英语知识 + - 如果你带一台运行 Windows、Mac 或 Linux 的笔记本电脑,以及可选的一台已越狱/root 的 iOS 或 Android 设备,那就太好了 + +- [FOSDEM 2016](https://fosdem.org/2016/schedule/track/testing_and_automation/): + [Testing interoperability with closed-source software through scriptable diplomacy](https://fosdem.org/2016/schedule/event/closed_source_interop/) + ([PDF]({{ site.baseurl_root }}/slides/fosdem-2016-testing-interoperability-with-closed-source-software-through-scriptable-diplomacy.pdf)) + + 你当然编写开源软件。他们没有。为了你的移动用户的利益,你们都需要成为朋友。进入 Frida,外交官(她实际上只是一个库,但不要告诉任何人)。她拥有哄骗的超能力,允许你通过暴露仅有二进制文件的软件的内部结构,无论是其他库、操作系统,还是你必须处理的其他 OS 进程。你可以编程 Frida 渗透闭源软件,并将其内部结构暴露为你可以用来测试软件互操作性的抽象。想将他们的一些逻辑提升到你的 mock 中吗?或者用你的 mock 替换他们二进制代码中的几个函数?希望你想使用高级语言(如 JavaScript 和/或 Python)来做这件事,因为这些是 Frida 最喜欢的。 + + 在本次演讲中,我们使用 Frida(可编写脚本的动态插桩工具包)来暴露仅有二进制文件的软件的内部功能。通过暴露内部函数和数据结构,紧密集成的软件通常变得更容易以细粒度进行测试。以前必须依赖于几个正在运行的子系统的较大集成测试,稍加努力,就可以变成更容易推理的隔离测试夹具。我们向那些必须处理这种级别互操作性的不幸灵魂展示如何编程 Frida 以识别和暴露远程进程中的函数,以及如何以单元测试风格将这些暴露的函数组合成小型测试夹具。 + +- [BSides Knoxville](https://bsidesknoxville.com/): + [Peeking under the hood with Frida](https://bsidesknoxville2016.sched.org/event/6tCd/peeking-under-the-hood-with-frida) + ([Recording](https://youtu.be/RINNW4xOWL8)) + + 有没有想过窥视运行在桌面或智能手机上的应用程序的底层?想知道传递给特定加密函数的数据是什么?Frida 适合你! + + Frida 是一个强大而现代的二进制插桩框架,它使得 hook 和跟踪目标可执行文件中的任意函数变得简单,并使用易于编写的 javascript 探索其功能。它就像二进制应用程序的 greasemonkey!它支持 Windows、Linux、OSX、iOS、Android 和 QNX。 + + 本次演讲将介绍 Frida 并展示如何使用它来辅助二进制应用程序的分析。它将包含大量演示。 + + 如果时间允许,我们还将讨论将 Frida 移植到 QNX 所需的一些工作。 + +- [Ekoparty 2016](https://www.ekoparty.org/): + [Getting fun with Frida](https://www.coresecurity.com/publication/getting-fun-frida) + + 你知道 Frida 是什么吗?你知道它是关于什么的吗?有什么用?不,我不是在说那位著名的画家。我说的是一个新的 hook 和动态二进制插桩框架。 + + 在这个快速演讲中,我打算向你展示这个新可用的框架,以促进一些日常逆向工程任务。我将教你它的基本部分是什么,为什么它对你有用,与其他类似框架相比有哪些优缺点,以及如何通过一些演示和代码片段使用它。 + +- [RMLL 2017](https://2017.rmll.info/en/): + [Unlocking secrets of proprietary software using Frida](https://prog2017.rmll.info/programme/securite-entre-transparence-et-opacite/devoilons-les-secrets-des-logiciels-proprietaires-avec-frida?lang=en) + ([Slides](http://slides.com/oleavr/frida-rmll-2017) + · [Recording](https://rmll.ubicast.tv/videos/frida_03038/)) + + 有没有想过了解运行在桌面或手机上的应用程序的内部结构?想知道传递给特定加密函数的数据是什么?那么 Frida 适合你! + + 本次演讲将介绍 Frida 并展示如何使用它来辅助二进制应用程序的分析。它将包含大量演示。 + +- [GPN 2018](https://entropia.de/GPN18): + [Frida - (Game)Hacking mit JavaScript](https://www.youtube.com/watch?v=6QpRD3tkw48) (German presentation) + + 每个人都知道 JavaScript。但是有多少人知道 JavaScript 也可以很好地用于破解游戏或程序?我介绍 Frida 框架,它正是实现了这一点! + + Frida 是一个允许将 JavaScript 加载到进程中的框架。为此,V8 JavaScript 解释器被加载到一个进程中,该进程带来了各种功能。内存操作、Hooks、Detours,一切皆有可能! diff --git a/_i18n/cn/_docs/quickstart.md b/_i18n/cn/_docs/quickstart.md new file mode 100644 index 00000000..3d1da32c --- /dev/null +++ b/_i18n/cn/_docs/quickstart.md @@ -0,0 +1,84 @@ +对于不耐烦的人,这里是如何使用 Frida 进行函数跟踪: + +{% highlight bash %} +~ $ pip install frida-tools +~ $ frida-trace -i "recv*" -i "read*" twitter +recv: Auto-generated handler: …/recv.js +# (snip) +recvfrom: Auto-generated handler: …/recvfrom.js +Started tracing 21 functions. Press Ctrl+C to stop. + 39 ms recv() + 112 ms recvfrom() + 128 ms recvfrom() + 129 ms recvfrom() +{% endhighlight %} + +如您所见,Frida 将自己注入到 Twitter 中,枚举了加载的共享库,并 hook 了所有名称以 `recv` 或 `read` 开头的函数。它还生成了一些样板脚本,用于在函数调用发生时检查它们。现在,这些脚本只是示例,您可以根据自己的喜好进行编辑,并且当它们在文件系统上更改时会自动重新加载。默认情况下,它们只是打印函数的名称,如上面的输出所示。 + +现在,让我们看看生成的 `recvfrom.js`: +{% highlight js %} +/* + * 由 Frida 自动生成。请修改以匹配 recvfrom 的签名。 + * + * 这个存根有点笨。Frida 的未来版本可以根据 OS API 参考、手册页等自动生成。(欢迎 Pull request!) + * + * 有关完整的 API 参考,请参阅: + * https://frida.re/docs/javascript-api/ + */ + +{ + /** + * 在即将调用 recvfrom 时同步调用。 + * + * @this {object} - 允许您存储要在 onLeave 中使用的状态的对象。 + * @param {function} log - 调用此函数以向用户显示字符串。 + * @param {array} args - 表示为 NativePointer 对象数组的函数参数。 + * 例如,如果第一个参数是指向 UTF-8 编码的 C 字符串的指针,则使用 args[0].readUtf8String()。 + * 也可以通过将 NativePointer 对象分配给此数组的元素来修改参数。 + * @param {object} state - 允许您跨函数调用保持状态的对象。 + * 一次只执行一个 JavaScript 函数,因此不必担心竞争条件。 + * 但是,不要使用它来跨 onEnter/onLeave 存储函数参数, + * 而是使用 "this",它是一个用于保持调用本地状态的对象。 + */ + onEnter(log, args, state) { + log("recvfrom()"); + }, + + /** + * 在即将从 recvfrom 返回时同步调用。 + * + * 详见 onEnter。 + * + * @this {object} - 允许您访问 onEnter 中存储的状态的对象。 + * @param {function} log - 调用此函数以向用户显示字符串。 + * @param {NativePointer} retval - 表示为 NativePointer 对象的返回值。 + * @param {object} state - 允许您跨函数调用保持状态的对象。 + */ + onLeave(log, retval, state) { + } +} +{% endhighlight %} + +现在,将 `log()` 行替换为以下内容: +{% highlight js %} +log("recvfrom(socket=" + args[0].toInt32() + + ", buffer=" + args[1] + + ", length=" + args[2].toInt32() + + ", flags=" + args[3] + + ", address=" + args[4] + + ", address_len=" + args[5].readPointer().toInt32() + + ")"); +{% endhighlight %} + +保存文件(它将自动重新加载)并在您的 Twitter 应用程序中执行一些操作以触发一些网络活动。您现在应该看到类似以下内容: + +{% highlight bash %} + 8098 ms recvfrom(socket=70, + buffer=0x32cc018, length=65536, + flags=0x0, + address=0xb0420bd8, address_len=16) +{% endhighlight %} + +不过这不算什么。当您开始使用 Python API 构建自己的工具时,真正的魔法才会发生,[frida-trace][] 就是基于该 API 构建的。 + +[frida-trace]: https://github.com/frida/frida-tools/blob/main/frida_tools/tracer.py diff --git a/_i18n/cn/_docs/stalker.md b/_i18n/cn/_docs/stalker.md new file mode 100644 index 00000000..d51b2721 --- /dev/null +++ b/_i18n/cn/_docs/stalker.md @@ -0,0 +1,2329 @@ +## 简介 + +Stalker 是 Frida 的代码跟踪引擎。它允许跟踪线程,捕获执行的每个函数、每个块,甚至每条指令。 +[这里](https://medium.com/@oleavr/anatomy-of-a-code-tracer-b081aadb0df8)提供了对 Stalker 引擎的非常好的概述, +我们建议你先仔细阅读。显然,实现在某种程度上是特定于架构的,尽管它们之间有很多共同之处。 +Stalker 目前支持运行 Android 或 iOS 的移动电话和平板电脑上常见的 AArch64 架构, +以及台式机和笔记本电脑上常见的 Intel 64 和 IA-32 架构。本页旨在将事情提升到下一个细节层次, +它剖析了 Stalker 的 ARM64 实现,并更详细地解释了它的工作原理。希望这可以帮助未来将 Stalker 移植到其他硬件架构的工作。 + +## 免责声明 + +虽然本文将涵盖 Stalker 内部工作的许多细节,但它不会非常详细地介绍回填(back-patching)。 +它旨在作为帮助他人理解该技术的起点,而 Stalker 已经足够复杂了,没有这个! +公平地说,这种复杂性并非没有原因,它是为了最小化本质上是昂贵操作的开销。 +最后,虽然本文将涵盖实现的关键概念,并将提取实现的一些关键部分进行逐行分析, +但仍会有一些实现的最后细节留给读者通过阅读[源代码](https://github.com/frida/frida-gum/blob/master/gum/backend-arm64/gumstalker-arm64.c)来发现。 +然而,希望它能证明是一个非常有用的起点。 + +## 目录 + + 1. [简介](#简介) + 1. [免责声明](#免责声明) + 1. [用例](#用例) + 1. [跟踪](#跟踪) + 1. [gum_stalker_follow_me](#gum_stalker_follow_me) + 1. [gum_stalker_follow](#gum_stalker_follow) + 1. [基本操作](#基本操作) + 1. [选项](#选项) + 1. [术语](#术语) + 1. [探针](#探针) + 1. [信任阈值](#信任阈值) + 1. [排除范围](#排除范围) + 1. [冻结/解冻](#冻结解冻) + 1. [调用指令](#调用指令) + 1. [帧](#帧) + 1. [转换器](#转换器) + 1. [Callouts](#callouts) + 1. [EOB/EOI](#eobeoi) + 1. [序言/尾声](#序言尾声) + 1. [计数器](#计数器) + 1. [Slabs](#slabs) + 1. [块](#块) + 1. [插桩块](#插桩块) + 1. [Helpers](#helpers) + 1. [last_stack_push](#last_stack_push) + 1. [last_stack_pop_and_go](#last_stack_pop_and_go) + 1. [上下文](#上下文) + 1. [上下文 Helpers](#上下文-helpers) + 1. [读取/写入上下文](#读取写入上下文) + 1. [控制流](#控制流) + 1. [Gates](#gates) + 1. [虚拟化函数](#虚拟化函数) + 1. [gum_exec_block_virtualize_branch_insn](#gum_exec_block_virtualize_branch_insn) + 1. [gum_exec_block_virtualize_ret_insn](#gum_exec_block_virtualize_ret_insn) + 1. [发出事件](#发出事件) + 1. [取消跟踪和清理](#取消跟踪和清理) + 1. [杂项](#杂项) + 1. [独占存储](#独占存储) + 1. [耗尽的块](#耗尽的块) + 1. [系统调用虚拟化](#系统调用虚拟化) + 1. [指针认证](#指针认证) +## 简介 + +Stalker 是 Frida 的代码跟踪引擎。它允许跟踪线程,捕获执行的每个函数、每个块,甚至每条指令。 +[这里](https://medium.com/@oleavr/anatomy-of-a-code-tracer-b081aadb0df8)提供了对 Stalker 引擎的非常好的概述, +我们建议你先仔细阅读。显然,实现在某种程度上是特定于架构的,尽管它们之间有很多共同之处。 +Stalker 目前支持运行 Android 或 iOS 的移动电话和平板电脑上常见的 AArch64 架构, +以及台式机和笔记本电脑上常见的 Intel 64 和 IA-32 架构。本页旨在将事情提升到下一个细节层次, +它剖析了 Stalker 的 ARM64 实现,并更详细地解释了它的工作原理。希望这可以帮助未来将 Stalker 移植到其他硬件架构的工作。 + +## 免责声明 + +虽然本文将涵盖 Stalker 内部工作的许多细节,但它不会非常详细地介绍回填(back-patching)。 +它旨在作为帮助他人理解该技术的起点,而 Stalker 已经足够复杂了,没有这个! +公平地说,这种复杂性并非没有原因,它是为了最小化本质上是昂贵操作的开销。 +最后,虽然本文将涵盖实现的关键概念,并将提取实现的一些关键部分进行逐行分析, +但仍会有一些实现的最后细节留给读者通过阅读[源代码](https://github.com/frida/frida-gum/blob/master/gum/backend-arm64/gumstalker-arm64.c)来发现。 +然而,希望它能证明是一个非常有用的起点。 + +## 目录 + + 1. [简介](#简介) + 1. [免责声明](#免责声明) + 1. [用例](#用例) + 1. [跟踪](#跟踪) + 1. [gum_stalker_follow_me](#gum_stalker_follow_me) + 1. [gum_stalker_follow](#gum_stalker_follow) + 1. [基本操作](#基本操作) + 1. [选项](#选项) + 1. [术语](#术语) + 1. [探针](#探针) + 1. [信任阈值](#信任阈值) + 1. [排除范围](#排除范围) + 1. [冻结/解冻](#冻结解冻) + 1. [调用指令](#调用指令) + 1. [帧](#帧) + 1. [转换器](#转换器) + 1. [Callouts](#callouts) + 1. [EOB/EOI](#eobeoi) + 1. [序言/尾声](#序言尾声) + 1. [计数器](#计数器) + 1. [Slabs](#slabs) + 1. [块](#块) + 1. [插桩块](#插桩块) + 1. [Helpers](#helpers) + 1. [last_stack_push](#last_stack_push) + 1. [last_stack_pop_and_go](#last_stack_pop_and_go) + 1. [上下文](#上下文) + 1. [上下文 Helpers](#上下文-helpers) + 1. [读取/写入上下文](#读取写入上下文) + 1. [控制流](#控制流) + 1. [Gates](#gates) + 1. [虚拟化函数](#虚拟化函数) + 1. [gum_exec_block_virtualize_branch_insn](#gum_exec_block_virtualize_branch_insn) + 1. [gum_exec_block_virtualize_ret_insn](#gum_exec_block_virtualize_ret_insn) + 1. [发出事件](#发出事件) + 1. [取消跟踪和清理](#取消跟踪和清理) + 1. [杂项](#杂项) + 1. [独占存储](#独占存储) + 1. [耗尽的块](#耗尽的块) + 1. [系统调用虚拟化](#系统调用虚拟化) + 1. [指针认证](#指针认证) + +## 用例 + +要开始理解 Stalker 的实现,我们必须首先详细了解它为用户提供了什么。 +虽然 Stalker 可以通过其原生 Gum 接口直接调用,但大多数用户将通过 [JavaScript API](https://frida.re/docs/javascript-api/#stalker) 调用它, +该 API 将代表他们调用这些 Gum 方法。Gum 的 [TypeScript 类型定义](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/frida-gum/index.d.ts)有很好的注释,并提供了更多细节。 + +从 JavaScript 到 Stalker 的主要 API 是: + +{% highlight js %} +Stalker.follow([threadId, options]) +{% endhighlight %} + +> 开始跟踪 `threadId`(如果省略则为当前线程) + +让我们考虑何时可以使用这些调用。当你提供线程 ID 进行跟踪时,可能是在你有一个感兴趣的线程并想知道它在做什么的情况下使用。 +也许它有一个有趣的名字?可以使用 `cat /proc/PID/tasks/TID/comm` 找到线程名称。 +或者你可能使用 Frida JavaScript API `Process.enumerateThreads()` 遍历了进程中的线程, +然后使用 NativeFunction 调用: + +{% highlight c %} +int pthread_getname_np(pthread_t thread, + char *name, size_t len); +{% endhighlight %} + +将此与 [Thread.backtrace()](https://frida.re/docs/javascript-api/#thread) 一起使用来转储线程堆栈, +可以让你真正了解进程在做什么。 + +你可能调用 `Stalker.follow()` 的另一种情况是从已被[拦截](https://frida.re/docs/javascript-api/#interceptor)或替换的函数中调用。 +在这种情况下,你找到了一个感兴趣的函数,并且想了解它的行为方式,你想看看在调用给定函数后线程采用哪些函数或甚至代码块。 +也许你想比较代码在不同输入下采用的方向,或者你想修改输入以查看是否可以让代码采用特定路径。 + +在这两种情况下,虽然 Stalker 必须在底层以稍微不同的方式工作,但它都由相同的简单 API `Stalker.follow()` 为用户管理。 + +## 跟踪 + +当用户调用 `Stalker.follow()` 时,在底层,JavaScript 引擎会调用 `gum_stalker_follow_me()` 来跟踪当前线程, +或调用 `gum_stalker_follow(thread_id)` 来跟踪进程中的另一个线程。 + +### gum_stalker_follow_me + +在 `gum_stalker_follow_me()` 的情况下,链接寄存器用于确定开始跟踪的指令。 +在 AArch64 架构中,链接寄存器(LR)设置为从函数调用返回后继续执行的指令地址, +它由诸如 BL 和 BLR 之类的指令设置为下一条指令的地址。由于只有一个链接寄存器, +如果被调用的函数要调用另一个例程,则必须存储 LR 的值(通常这将在堆栈上)。 +此值随后将从堆栈加载回寄存器,并使用 RET 指令将控制权返回给调用者。 + +让我们看看 `gum_stalker_follow_me()` 的代码。这是函数原型: + +{% highlight c %} +GUM_API void gum_stalker_follow_me (GumStalker * self, + GumStalkerTransformer * transformer, GumEventSink * sink); +{% endhighlight %} + +所以我们可以看到该函数由 QuickJS 或 V8 运行时调用,传递 3 个参数。 +第一个是 Stalker 实例本身。请注意,如果一次加载多个脚本,可能会有多个这样的实例。 +第二个是转换器,这可以用于在编写插桩代码时转换它(稍后会详细介绍)。 +最后一个参数是事件接收器,这是 Stalker 引擎运行时生成的事件传递到的地方。 + +{% highlight asm %} +#ifdef __APPLE__ + .globl _gum_stalker_follow_me +_gum_stalker_follow_me: +#else + .globl gum_stalker_follow_me + .type gum_stalker_follow_me, %function +gum_stalker_follow_me: +#endif + stp x29, x30, [sp, -16]! + mov x29, sp + mov x3, x30 +#ifdef __APPLE__ + bl __gum_stalker_do_follow_me +#else + bl _gum_stalker_do_follow_me +#endif + ldp x29, x30, [sp], 16 + br x0 +{% endhighlight %} + +我们可以看到第一条指令 STP 将一对寄存器存储到堆栈上。我们可以注意到表达式 `[sp, -16]!`。 +这是一个[预递减](https://thinkingeek.com/2017/05/29/exploring-aarch64-assembler-chapter-8/), +这意味着首先将堆栈前进 16 字节,然后存储两个 8 字节寄存器值。 +我们可以在函数底部看到相应的指令 `ldp x29, x30, [sp], 16`。这是将这两个寄存器值从堆栈恢复到寄存器中。 +但这两个寄存器是什么? + +嗯,`X30` 是链接寄存器,`X29` 是帧指针寄存器。回想一下,如果我们希望调用另一个函数, +我们必须将链接寄存器存储到堆栈,因为这将导致它被覆盖,我们需要这个值才能返回到我们的调用者。 + +帧指针用于指向调用函数时堆栈的顶部,以便可以以相对于帧指针的固定偏移量访问所有堆栈传递的参数和基于堆栈的局部变量。 +同样,我们需要保存和恢复它,因为每个函数都有自己的这个寄存器的值,所以我们需要存储调用者放入其中的值, +并在返回之前恢复它。实际上,你可以在下一条指令 `mov x29, sp` 中看到我们将帧指针设置为当前堆栈指针。 + +我们可以看到下一条指令 `mov x3, x30`,将链接寄存器的值放入 X3。 +AArch64 上的前 8 个参数在寄存器 X0-X7 中传递。所以这被放入用于第四个参数的寄存器中。 +然后我们调用(带链接的分支)函数 `_gum_stalker_do_follow_me()`。 +所以我们可以看到我们将前三个参数在 X0-X2 中原封不动地传递, +以便 `_gum_stalker_do_follow_me()` 接收与我们被调用时相同的值。 +最后,我们可以看到在此函数返回后,我们分支到我们作为其返回值接收的地址。 +(在 AArch64 中,函数的返回值在 X0 中返回)。 + +{% highlight c %} +gpointer +_gum_stalker_do_follow_me (GumStalker * self, + GumStalkerTransformer * transformer, + GumEventSink * sink, + gpointer ret_addr) +{% endhighlight %} + +### gum_stalker_follow + +此例程的原型与 `gum_stalker_follow_me()` 非常相似,但有额外的 `thread_id` 参数。 +实际上,如果要求跟踪当前线程,那么它将调用该函数。不过让我们看看指定另一个线程 ID 时的情况。 + +{% highlight c %} +void +gum_stalker_follow (GumStalker * self, + GumThreadId thread_id, + GumStalkerTransformer * transformer, + GumEventSink * sink) +{ + if (thread_id == gum_process_get_current_thread_id ()) + { + gum_stalker_follow_me (self, transformer, sink); + } + else + { + GumInfectContext ctx; + + ctx.stalker = self; + ctx.transformer = transformer; + ctx.sink = sink; + + gum_process_modify_thread (thread_id, gum_stalker_infect, &ctx); + } +} +{% endhighlight %} + +我们可以看到这调用了函数 `gum_process_modify_thread()`。这不是 Stalker 的一部分,而是 Gum 本身的一部分。 +此函数接受一个带有上下文参数的回调来调用,传递线程上下文结构。然后此回调可以修改 `GumCpuContext` 结构, +`gum_process_modify_thread()` 将写回更改。我们可以在下面看到上下文结构, +如你所见,它包含 AArch64 CPU 中所有寄存器的字段。我们还可以在下面看到我们的回调的函数原型。 + +{% highlight c %} +typedef GumArm64CpuContext GumCpuContext; + +struct _GumArm64CpuContext +{ + guint64 pc; + guint64 sp; + + guint64 x[29]; + guint64 fp; + guint64 lr; + guint8 q[128]; +}; +{% endhighlight %} + +{% highlight c %} +static void +gum_stalker_infect (GumThreadId thread_id, + GumCpuContext * cpu_context, + gpointer user_data) +{% endhighlight %} + +那么,`gum_process_modify_thread()` 是如何工作的?嗯,这取决于平台。 +在 Linux(和 Android)上,它使用 `ptrace` API(GDB 使用的同一个)来附加到线程并读写寄存器。 +但有很多复杂性。在 Linux 上,你不能 ptrace 自己的进程(或实际上同一进程组中的任何进程), +所以 Frida 在其自己的进程组中创建当前进程的克隆并共享相同的内存空间。 +它使用 UNIX 套接字与它通信。这个克隆的进程充当调试器,读取原始目标进程的寄存器并将它们存储在共享内存空间中, +然后按需将它们写回进程。哦,还有 `PR_SET_DUMPABLE` 和 `PR_SET_PTRACER`,它们控制谁被允许 ptrace 我们的原始进程的权限。 + +现在你会看到 `gum_stalker_infect()` 的功能实际上与我们之前提到的 `_gum_stalker_do_follow_me()` 非常相似。 +两个函数本质上执行相同的工作,尽管 `_gum_stalker_do_follow_me()` 在目标线程上运行, +但 `gum_stalker_infect()` 不是,所以它必须编写一些代码供目标线程调用, +使用 [GumArm64Writer](https://github.com/frida/frida-gum/blob/master/gum/arch-arm64/gumarm64writer.c) 而不是直接调用函数。 + +我们将很快更详细地介绍这些函数,但首先我们需要更多的背景知识。 +## 基本操作 + +代码可以被认为是一系列指令块(也称为基本块)。每个块以一系列可选的指令开始(我们可能有两个连续的分支语句), +这些指令按顺序运行,并在我们遇到导致(或可能导致)执行继续使用内存中紧随其后的指令以外的指令时结束。 + +Stalker 一次处理一个块。它从调用 `gum_stalker_follow_me()` 的返回后的块开始, +或者从调用 `gum_stalker_follow()` 时目标线程的指令指针指向的代码块开始。 + +Stalker 的工作方式是分配一些内存并向其中写入原始块的新插桩副本。 +可以添加指令来生成事件,或执行 Stalker 引擎提供的任何其他功能。 +Stalker 还必须根据需要重定位指令。考虑以下指令: + +> ADR +> 在 PC 相对偏移处的标签地址。 +> +> ADR Xd, label +> +> Xd +> 是通用目标寄存器的 64 位名称,范围为 0 到 31。 +> +> label +> 是要计算其地址的程序标签。 +> 它是从此指令地址的偏移量, +> 范围为 ±1MB。 + +如果将此指令复制到内存中的不同位置并执行,那么因为标签的地址是通过将偏移量添加到当前指令指针来计算的, +所以值会不同。幸运的是,Gum 有一个 [Relocator](https://github.com/frida/frida-gum/blob/master/gum/arch-arm64/gumarm64relocator.c) +正是为了这个目的,它能够根据指令的新位置修改指令,以便计算正确的地址。 + +现在,回想一下我们说 Stalker 一次处理一个块。那么,我们如何插桩下一个块呢? +我们还记得每个块也以分支指令结束,好吧,如果我们修改这个分支以分支回 Stalker 引擎, +但确保我们存储分支打算结束的目的地,我们可以插桩下一个块并将执行重定向到那里。 +这个简单的过程可以一个接一个地继续。 + +现在,这个过程可能有点慢,所以我们可以应用一些优化。首先,如果我们多次执行同一个代码块 +(例如循环,或者只是多次调用的函数),我们不必一遍又一遍地重新插桩它。 +我们可以重新执行相同的插桩代码。因此,保留了一个哈希表,其中包含我们之前遇到的所有块以及我们放置块的插桩副本的位置。 + +其次,当遇到调用指令时,在发出插桩调用后,我们然后发出一个着陆垫(landing pad), +我们可以返回到该着陆垫而无需重新进入 Stalker。Stalker 构建一个侧栈, +使用 `GumExecFrame` 结构记录真实的返回地址(`real_address`)和这个着陆垫(`code_address`)。 +当函数返回时,我们发出代码,将检查侧栈中的返回地址与 `real_address` 进行比较, +如果匹配,它可以简单地返回到 `code_address` 而无需重新进入运行时。 +这个着陆垫最初将包含进入 Stalker 引擎以插桩下一个块的代码,但稍后可以回填以直接分支到此块。 +这意味着整个返回序列可以在不进入和离开 Stalker 的开销的情况下处理。 + +如果返回地址与 `GumExecFrame` 的 `real_address` 存储的不匹配,或者我们在侧栈中用完空间, +我们只需从头开始构建一个新的。我们需要在应用程序代码执行时保留 LR 的值, +以便应用程序不能使用它来检测 Stalker 的存在(反调试), +或者以防它将其用于除简单返回之外的任何其他目的(例如引用代码段中的内联数据)。 +此外,我们希望 Stalker 能够随时取消跟踪,所以我们不想回到我们的堆栈上纠正我们沿途修改的 LR 值。 + +最后,虽然我们总是用对 Stalker 的调用替换分支以插桩下一个块, +但根据 `Stalker.trustThreshold` 的配置,我们可能会*回填*这样的插桩代码, +用直接分支到下一个插桩块来替换调用。确定性分支(例如目的地是固定的并且分支不是条件的)很简单, +我们可以用一个到下一个块的分支替换到 Stalker 的分支。但我们也可以处理条件分支, +如果我们插桩两个代码块(如果采用分支则为一个,如果不采用则为另一个)。 +然后我们可以用一个条件分支替换原始条件分支,该条件分支将控制流引导到采用分支时遇到的块的插桩版本, +然后是到另一个插桩块的无条件分支。我们还可以部分处理目标不是静态的分支。 +假设我们的分支是这样的: + +{% highlight asm %} +br x0 +{% endhighlight %} + +这种指令在调用函数指针或类方法时很常见。虽然 X0 的值可以改变,但通常它实际上总是相同的。 +在这种情况下,我们可以用将 X0 的值与我们已知的函数进行比较的代码替换最终的分支指令, +如果匹配,则分支到代码的插桩副本的地址。然后可以跟随一个无条件分支回到 Stalker 引擎(如果不匹配)。 +所以如果函数指针的值比如说被改变了,那么代码仍然可以工作,我们将重新进入 Stalker 并插桩我们最终到达的任何地方。 +但是,如果正如我们所期望的那样它保持不变,那么我们可以完全绕过 Stalker 引擎并直接进入插桩函数。 + +## 选项 + +现在让我们看看使用 Stalker 跟踪线程时的选项。当跟踪的线程正在执行时,Stalker 会生成事件, +这些事件被放置到队列中,并定期或由用户手动刷新。这不是由 Stalker 本身完成的, +而是由 `EventSink::process` vfunc 完成的,因为重新进入 JavaScript 运行时一次处理一个事件会非常昂贵。 +大小和时间段可以通过选项配置。可以基于每条指令生成事件,用于调用、返回或所有指令。 +或者可以基于块生成它们,当块被执行时,或者当它被 Stalker 引擎插桩时。 + +我们还可以提供两个回调之一 `onReceive` 或 `onCallSummary`。 +前者将简单地传递一个包含 Stalker 生成的原始事件的二进制 blob,事件按生成顺序排列。 +(`Stalker.parse()` 可用于将其转换为表示事件的元组的 JS 数组。) +第二个聚合这些结果,简单地返回每个函数被调用次数的计数。这比 `onReceive` 更有效,但数据的粒度要低得多。 + +## 术语 + +在我们继续描述 Stalker 的详细实现之前,我们首先需要了解设计中使用的一些关键术语和概念。 + +### 探针 + +当线程在 Stalker 之外运行时,你可能熟悉使用 `Interceptor.attach()` 在调用给定函数时获得回调。 +但是,当线程在 Stalker 中运行时,这些拦截器可能不起作用。这些拦截器通过修补目标函数的前几条指令(序言) +来将执行重定向到 Frida 中来工作。Frida 复制并重定位这些前几条指令到其他地方, +以便在 `onEnter` 回调完成后,它可以将控制流重定向回原始函数。 + +这些在 Stalker 中可能不起作用的原因很简单,原始函数从未被调用。 +每个块在执行之前都在内存中的其他地方插桩,并且执行的是这个副本。 +Stalker 支持 API 函数 `Stalker.addCallProbe(address, callback[, data])` 来提供此功能。 +如果我们的 `Interceptor` 在块被插桩之前已附加,或者 Stalker 的 `trustThreshold` 配置为我们的块将被重新插桩, +那么我们的 `Interceptor` 将起作用(因为修补的指令将被复制到新的插桩块)。否则它不会。 +当然,我们希望能够在不满足这些条件时支持钩子函数。API 的普通用户可能不熟悉设计的这个细节级别, +因此调用探针解决了这个问题。 + +可选的 data 参数在注册探针回调时传递,并将在执行时传递给回调例程。 +因此,此指针需要存储在 Stalker 引擎中。此外,需要存储地址,以便当遇到调用函数的指令时, +可以将代码插桩为首先调用该函数。由于多个函数可能调用你添加探针的函数, +因此许多插桩块可能包含调用探针函数的附加指令。因此,每当添加或删除探针时, +缓存的插桩块都会被销毁,因此所有代码都必须重新插桩。请注意,此 data 参数仅在 `callback` 是 C 回调时使用 +——例如使用 `CModule` 实现——因为当使用 JavaScript 时,使用闭包来捕获任何所需的状态更简单。 + +### 信任阈值 + +回想一下,我们应用的简单优化之一是,如果我们尝试多次执行一个块, +在后续场合,我们可以简单地调用我们上次创建的插桩块?好吧,这只有在我们正在插桩的代码没有改变的情况下才有效。 +在自修改代码的情况下(这通常用作反调试/反反汇编技术,试图阻止对安全关键代码的分析), +代码可能会改变,因此无法重用插桩块。那么,我们如何检测块是否已更改? +我们只需在数据结构中保留原始代码的副本以及插桩版本。然后,当我们再次遇到一个块时, +我们可以将我们要插桩的代码与我们上次插桩的版本进行比较,如果它们匹配,我们可以重用该块。 +但是每次块运行时执行比较可能会减慢速度。所以,这又是一个可以自定义 Stalker 的领域。 + +> `Stalker.trustThreshold`: 一个整数,指定在假定代码可以信任不会变异之前需要执行多少次。 +> 指定 -1 表示不信任(慢),0 表示从一开始就信任代码,N 表示在代码执行 N 次后信任代码。默认为 1。 + +实际上,N 的值是块需要重新执行并与先前插桩的块匹配(例如未更改)的次数, +然后我们才停止执行比较。请注意,即使信任阈值设置为 `-1` 或 `0`,仍会存储代码块的原始副本。 +虽然这些值实际上不需要它,但为了保持简单而保留了它。无论如何,这两个都不是默认设置。 + +### 排除范围 + +Stalker 还有 API `Stalker.exclude(range)`,它传递一个基址和限制,用于防止 Stalker 插桩这些区域内的代码。 +例如,考虑你的线程在 `libc` 内部调用 `malloc()`。你很可能不关心堆的内部工作, +这不仅会降低性能,而且还会生成大量你不关心的无关事件。但是,需要考虑的一件事是, +一旦调用排除范围,该线程的跟踪就会停止,直到它返回。这意味着,如果该线程要调用不在受限范围内的函数, +例如回调,那么 Stalker 将不会捕获它。正如这可以用于停止整个库的跟踪一样, +它也可以用于停止跟踪给定函数(及其被调用者)。如果你的目标应用程序是静态链接的,这可能特别有用。 +在这里,我们不能简单地忽略对 `libc` 的所有调用,但我们可以使用 `Module.enumerateSymbols()` 找到 `malloc()` 的符号并忽略该单个函数。 + +### 冻结/解冻 + +作为 DEP 的扩展,一些系统防止页面同时标记为可写和可执行。 +因此,Frida 必须在可写和可执行之间切换页面权限,以写入插桩代码,并允许该代码分别执行。 +当页面可执行时,它们被称为冻结(因为它们不能被更改),当它们再次变为可写时,它们被认为是解冻的。 + +### 调用指令 + +与 Intel 不同,AArch64 没有单个显式的 `CALL` 指令,该指令具有不同的形式以应对所有支持的场景。 +相反,它使用许多不同的指令来提供对函数调用的支持。这些指令都分支到给定位置并使用返回地址更新链接寄存器 `LR`: + +* `BL` +* `BLR` +* `BLRAA` +* `BLRAAZ` +* `BLRAB` +* `BLRABZ` + +为简单起见,在本文的其余部分,我们将这些指令集合称为"调用指令"。 + +### 帧 + +每当 Stalker 遇到调用时,它都会将返回地址和插桩返回块转发器的地址存储在结构中, +并将这些添加到存储在其自己的数据结构中的堆栈中。它将此用作推测性优化, +并且还用作启发式方法,以在发出调用和返回事件时近似调用深度。 + +{% highlight c %} +typedef struct _GumExecFrame GumExecFrame; + +struct _GumExecFrame +{ + gpointer real_address; + gpointer code_address; +}; +{% endhighlight %} + +### 转换器 + +`GumStalkerTransformer` 类型用于生成插桩代码。默认转换器的实现如下所示: + +{% highlight c %} +static void +gum_default_stalker_transformer_transform_block ( + GumStalkerTransformer * transformer, + GumStalkerIterator * iterator, + GumStalkerOutput * output) +{ + while (gum_stalker_iterator_next (iterator, NULL)) + { + gum_stalker_iterator_keep (iterator); + } +} +{% endhighlight %} + +它由负责生成插桩代码的函数 `gum_exec_ctx_obtain_block_for()` 调用, +其工作是生成插桩代码。我们可以看到它使用循环一次处理一条指令。 +首先从迭代器检索一条指令,然后告诉 Stalker 按原样插桩指令(不修改)。 +这两个函数在 Stalker 本身内部实现。第一个负责解析 `cs_insn` 并更新内部状态。 +此 `cs_insn` 类型是内部 [Capstone](http://www.capstone-engine.org/) 反汇编器用于表示指令的数据类型。 +第二个负责写出插桩指令(或指令集)。我们稍后将更详细地介绍这些。 + +用户可以提供自定义实现来替换默认转换器,该实现可以随意替换和插入指令。 +[API 文档](https://frida.re/docs/javascript-api/#stalker)中提供了一个很好的示例。 + +### Callouts + +转换器还可以进行 callouts。也就是说,它们指示 Stalker 发出指令以调用 JavaScript 函数 +——或纯 C 回调,例如使用 CModule 实现——传递 CPU 上下文和可选的上下文参数。 +然后此函数能够随意修改或检查寄存器。此信息存储在 `GumCallOutEntry` 中。 + +{% highlight c %} +typedef void (* GumStalkerCallout) (GumCpuContext * cpu_context, + gpointer user_data); + +typedef struct _GumCalloutEntry GumCalloutEntry; + +struct _GumCalloutEntry +{ + GumStalkerCallout callout; + gpointer data; + GDestroyNotify data_destroy; + + gpointer pc; + + GumExecCtx * exec_context; +}; +{% endhighlight %} + +### EOB/EOI + +回想一下,[Relocator](https://github.com/frida/frida-gum/blob/master/gum/arch-arm64/gumarm64relocator.c) +在生成插桩代码中发挥着重要作用。它有两个重要的属性来控制其状态。 + +块结束(EOB)表示已到达块的末尾。当我们遇到*任何*分支指令时会发生这种情况。分支、调用或返回指令。 + +输入结束(EOI)表示我们不仅已到达块的末尾,而且可能已到达输入的末尾, +即此指令之后可能不是另一条指令。虽然对于调用指令来说不是这种情况, +因为当被调用者返回时代码控制将(通常)传递回来,因此必须有更多指令跟随。 +(请注意,编译器通常会为调用非返回函数(如 `exit()`)生成分支指令。) +虽然不能保证调用指令后有有效指令,但我们可以推测性地优化这种情况。 +如果我们遇到非条件分支指令或返回指令,很可能之后不会有代码。 + +### 序言/尾声 + +当控制流从程序重定向到 Stalker 引擎时,必须保存 CPU 的寄存器,以便 Stalker 可以运行并使用寄存器, +并在控制传递回程序之前恢复它们,以便不会丢失任何状态。 + +AArch64 的[过程调用标准](https://static.docs.arm.com/den0024/a/DEN0024A_v8_architecture_PG.pdf) +规定某些寄存器(特别是 X19 到 X29)是被调用者保存的寄存器。 +这意味着当编译器生成使用这些寄存器的代码时,它必须首先存储它们。 +因此,严格来说没有必要将这些寄存器保存到上下文结构中,因为如果它们被 Stalker 引擎内的代码使用, +它们将被恢复。这个*"最小"*上下文对于大多数目的来说是足够的。 + +但是,如果 Stalker 引擎要调用由 `Stalker.addCallProbe()` 注册的探针, +或由 `iterator.putCallout()`(由转换器调用)创建的 callout, +那么这些回调将期望接收完整的 CPU 上下文作为参数。 +他们将期望能够修改此上下文,并且更改在控制传递回应用程序代码时生效。 +因此,对于这些实例,我们必须编写一个*"完整"*上下文, +其布局必须与结构 `GumArm64CpuContext` 规定的预期格式匹配。 + +{% highlight c %} +typedef struct _GumArm64CpuContext GumArm64CpuContext; + +struct _GumArm64CpuContext +{ + guint64 pc; + guint64 sp; /* X31 */ + guint64 x[29]; + guint64 fp; /* X29 - frame pointer */ + guint64 lr; /* X30 */ + guint8 q[128]; /* FPU, NEON (SIMD), CRYPTO regs */ +}; +{% endhighlight %} + +但是请注意,在任何一种情况下写出必要的 CPU 寄存器(序言)所需的代码都相当长(数十条指令)。 +之后恢复它们的代码(尾声)长度相似。我们不想在我们插桩的每个块的开头和结尾写这些。 +因此,我们将这些(以与我们编写插桩块相同的方式)写入公共内存位置, +并在每个插桩块的开头和结尾简单地发出调用指令来调用这些函数。 +这些公共内存位置称为 *helpers*。以下函数创建这些序言和尾声。 + +{% highlight c %} +static void gum_exec_ctx_write_minimal_prolog_helper ( + GumExecCtx * ctx, GumArm64Writer * cw); + +static void gum_exec_ctx_write_minimal_epilog_helper ( + GumExecCtx * ctx, GumArm64Writer * cw); + +static void gum_exec_ctx_write_full_prolog_helper ( + GumExecCtx * ctx, GumArm64Writer * cw); + +static void gum_exec_ctx_write_full_epilog_helper ( + GumExecCtx * ctx, GumArm64Writer * cw); +{% endhighlight %} + +最后,请注意在 AArch64 架构中,只能直接分支到调用者 ±128 MB 内的代码, +并且使用间接分支更昂贵(在代码大小和性能方面)。因此,随着我们编写越来越多的插桩块, +我们将离共享序言和尾声越来越远。如果我们距离超过 128 MB, +我们只需写出这些序言和尾声的另一个副本以供使用。这给了我们一个非常合理的权衡。 + +### 计数器 + +最后,有一系列计数器,你可以看到它们记录在插桩块末尾遇到的每种类型指令的数量。 +这些仅由测试套件使用,以在性能调优期间指导开发人员,指示哪些分支类型最常需要完整的上下文切换到 Stalker 以解析目标。 +## Slabs + +现在让我们看看 Stalker 将其插桩代码存储在哪里,在 slabs 中。 +下面是用于保存所有内容的数据结构: + +{% highlight c %} +typedef guint8 GumExecBlockFlags; +typedef struct _GumExecBlock GumExecBlock; +typedef struct _GumSlab GumSlab; + +struct _GumExecBlock +{ + GumExecCtx * ctx; + GumSlab * slab; + + guint8 * real_begin; + guint8 * real_end; + guint8 * real_snapshot; + guint8 * code_begin; + guint8 * code_end; + + GumExecBlockFlags flags; + gint recycle_count; +}; + +struct _GumSlab +{ + guint8 * data; + guint offset; + guint size; + GumSlab * next; + + guint num_blocks; + GumExecBlock blocks[]; +}; + +enum _GumExecBlockFlags +{ + GUM_EXEC_ACTIVATION_TARGET = (1 << 0), +}; +{% endhighlight %} + +现在让我们看看 Stalker 初始化时配置其大小的一些代码: + +{% highlight c %} +#define GUM_CODE_SLAB_MAX_SIZE (4 * 1024 * 1024) +#define GUM_EXEC_BLOCK_MIN_SIZE 1024 + +static void +gum_stalker_init (GumStalker * self) +{ + ... + + self->page_size = gum_query_page_size (); + self->slab_size = + GUM_ALIGN_SIZE (GUM_CODE_SLAB_MAX_SIZE, self->page_size); + self->slab_header_size = + GUM_ALIGN_SIZE (GUM_CODE_SLAB_MAX_SIZE / 12, self->page_size); + self->slab_max_blocks = (self->slab_header_size - + G_STRUCT_OFFSET (GumSlab, blocks)) / sizeof (GumExecBlock); + + ... +} +{% endhighlight %} + +所以我们可以看到每个 slab 的大小为 4 MB。这个 slab 的 1/12 保留给其头部, +即 `GumSlab` 结构本身,包括其 `GumExecBlock` 数组。请注意,这被定义为 `GumSlab` 结构末尾的零长度数组, +但实际可以放入 slab 头部的这些数量被计算并存储在 `slab_max_blocks` 中。 + +那么 slab 的其余部分用于什么?虽然 slab 的头部用于所有会计信息, +但 slab 的其余部分(以下称为尾部)用于插桩指令本身(它们内联存储在 slab 中)。 + +那么为什么将 slab 的 1/12 分配给头部,其余部分分配给指令? +好吧,要插桩的每个块的长度会有很大差异,并且可能会受到所使用的编译器及其优化设置的影响。 +一些粗略的经验测试表明,鉴于每个块的平均长度,这可能是一个合理的比率, +以确保我们不会在尾部用完新插桩块的空间之前用完新 `GumExecBlock` 条目的空间,反之亦然。 + +现在让我们看看创建它们的代码: + +{% highlight c %} +static GumSlab * +gum_exec_ctx_add_slab (GumExecCtx * ctx) +{ + GumSlab * slab; + GumStalker * stalker = ctx->stalker; + + slab = gum_memory_allocate (NULL, stalker->slab_size, + stalker->page_size, + stalker->is_rwx_supported ? GUM_PAGE_RWX : GUM_PAGE_RW); + + slab->data = (guint8 *) slab + stalker->slab_header_size; + slab->offset = 0; + slab->size = stalker->slab_size - stalker->slab_header_size; + slab->next = ctx->code_slab; + + slab->num_blocks = 0; + + ctx->code_slab = slab; + + return slab; +} +{% endhighlight %} + +在这里,我们可以看到 `data` 字段指向头部之后可以写入指令的尾部的开始。 +`offset` 字段跟踪我们在尾部中的偏移量。`size` 字段跟踪尾部中可用的总字节数。 +`num_blocks` 字段跟踪已写入 slab 的插桩块数量。 + +请注意,在可能的情况下,我们使用 RWX 权限分配 slab,这样我们就不必一直冻结和解冻它。 +在支持 RWX 的系统上,冻结和解冻函数变为空操作。 + +最后,我们可以看到每个 slab 都包含一个 `next` 指针,可用于将 slabs 链接在一起形成单链表。 +这用于我们可以遍历它们并在 Stalker 完成时处理它们。 + +## 块 + +现在我们了解了 slabs 的工作原理。让我们更详细地看看块。 +正如我们所知,我们可以在 slab 中存储多个块,并将它们的指令写入尾部。让我们看看分配新块的代码: + +{% highlight c %} +static GumExecBlock * +gum_exec_block_new (GumExecCtx * ctx) +{ + GumStalker * stalker = ctx->stalker; + GumSlab * slab = ctx->code_slab; + gsize available; + + available = (slab != NULL) ? slab->size - slab->offset : 0; + if (available >= GUM_EXEC_BLOCK_MIN_SIZE && + slab->num_blocks != stalker->slab_max_blocks) + { + GumExecBlock * block = slab->blocks + slab->num_blocks; + + block->ctx = ctx; + block->slab = slab; + + block->code_begin = slab->data + slab->offset; + block->code_end = block->code_begin; + + block->flags = 0; + block->recycle_count = 0; + + gum_stalker_thaw (stalker, block->code_begin, available); + slab->num_blocks++; + + return block; + } + + if (stalker->trust_threshold < 0 && slab != NULL) + { + slab->offset = 0; + + return gum_exec_block_new (ctx); + } + + gum_exec_ctx_add_slab (ctx); + + gum_exec_ctx_ensure_inline_helpers_reachable (ctx); + + return gum_exec_block_new (ctx); +} +{% endhighlight %} + +该函数首先检查 slab 尾部是否有最小大小块的空间(1024 字节), +以及 slab 头部的 `GumExecBlocks` 数组中是否有新条目的空间。 +如果有,则在数组中创建一个新条目,并设置其指针以引用 `GumExecCtx`(主 Stalker 会话上下文)和 `GumSlab`。 +`code_begin` 和 `code_end` 指针都设置为尾部中的第一个空闲字节。 +信任阈值机制使用的 `recycle_count` 用于确定块未修改遇到的次数,重置为零, +并且尾部的其余部分被解冻以允许将代码写入其中。 + +接下来,如果信任阈值设置为小于零(回想一下 -1 意味着块永远不被信任并且总是重写), +那么我们重置 slab `offset`(指向尾部中第一个空闲字节的指针)并重新开始。 +这意味着为 slab 内任何块编写的任何插桩代码都将被覆盖。 + +最后,由于当前 slab 中没有剩余空间,并且我们不能覆盖它,因为信任阈值意味着块可能会被重用, +那么我们必须通过调用我们上面看到的 `gum_exec_ctx_add_slab()` 来分配一个新的 slab。 +然后我们调用 `gum_exec_ctx_ensure_inline_helpers_reachable()`,稍后会详细介绍, +然后我们从新的 slab 分配我们的块。 + +回想一下,我们使用 *helpers*(例如保存和恢复 CPU 上下文的序言和尾声) +来防止必须在每个块的开头和结尾复制这些指令。由于我们需要能够从我们正在写入 slab 的插桩代码中调用这些, +并且我们使用只能从调用站点到达 ±128 MB 的直接分支来执行此操作,因此我们需要确保我们可以到达它们。 +如果我们以前没有写过它们,那么我们将它们写入我们当前的 slab。 +请注意,这些 helper 函数需要从 slab 尾部中写入的任何插桩指令中可达。 +因为我们的 slab 只有 4 MB 大小,所以如果我们的 helpers 写在我们当前的 slab 中,那么它们将很好地可达。 +如果我们正在分配后续 slab 并且它足够接近前一个 slab(我们只保留我们上次写入 helper 函数的位置), +那么我们可能不需要再次写出它们,可以依赖附近 slab 中的先前副本。 +请注意,我们受 `mmap()` 的支配,因为我们的 slab 在虚拟内存中的分配位置, +ASLR 可能决定我们的 slab 最终不在前一个附近的任何地方。 + +我们只能假设这不太可能成为问题,或者这已经被考虑到 slabs 的大小中, +以确保将 helpers 写入每个 slab 不会有太大的开销,因为它不会使用它们空间的很大一部分。 +另一种选择可能是每次写出 helper 函数时存储每个位置,以便我们有更多候选者可供选择 +(也许我们的 slab 没有分配在先前分配的 slab 附近,但也许它足够接近其他 slab 之一)。 +否则,我们可以考虑使用 `mmap()` 制作自定义分配器来保留一个大的(例如 128 MB)虚拟地址空间区域, +然后根据需要再次使用 `mmap()` 一次提交一个 slab 的内存。但这些想法可能都有点过头了。 + +## 插桩块 + +插桩代码块的主要函数称为 `gum_exec_ctx_obtain_block_for()`。 +它首先在哈希表中查找现有块,该哈希表以插桩的原始块的地址为索引。 +如果它找到一个并且满足围绕信任阈值的上述约束,那么它可以简单地返回。 + +`GumExecBlock` 的字段使用如下。`real_begin` 设置为要插桩的原始代码块的开始。 +`code_begin` 字段指向尾部的第一个空闲字节(记住这是由上面讨论的 `gum_exec_block_new()` 函数设置的)。 +初始化 `GumArm64Relocator` 以从 `real_begin` 处的原始代码读取代码, +并初始化 `GumArm64Writer` 以将其输出写入从 `code_begin` 开始的 slab。 +这些项目中的每一个都打包到 `GumGeneratorContext` 中,最后用于构造 `GumStalkerIterator`。 + +然后将此迭代器传递给转换器。回想一下默认实现如下: + +{% highlight c %} +static void +gum_default_stalker_transformer_transform_block ( + GumStalkerTransformer * transformer, + GumStalkerIterator * iterator, + GumStalkerOutput * output) +{ + while (gum_stalker_iterator_next (iterator, NULL)) + { + gum_stalker_iterator_keep (iterator); + } +} +{% endhighlight %} + +我们现在将略过 `gum_stalker_iterator_next()` 和 `gum_stalker_iterator_keep()` 的细节。 +但本质上,这会导致迭代器一次从重定位器读取一条指令的代码,并使用写入器写出重定位的指令。 +在此过程之后,可以更新 `GumExecBlock` 结构。其字段 `real_end` 可以设置为重定位器读取到的地址, +其字段 `code_end` 可以设置为写入器写入到的地址。因此 `real_begin` 和 `real_end` 标记原始块的限制, +`code_begin` 和 `code_end` 标记新插桩块的限制。最后,`gum_exec_ctx_obtain_block_for()` 调用 `gum_exec_block_commit()`, +它获取原始块的副本并将其放在插桩副本之后。字段 `real_snapshot` 指向此(因此与 `code_end` 相同)。 +接下来,更新 slab 的 `offset` 字段以反映我们的插桩块和原始代码副本使用的空间。最后,冻结块以允许执行它。 + +{% highlight c %} +static void +gum_exec_block_commit (GumExecBlock * block) +{ + gsize code_size, real_size; + + code_size = block->code_end - block->code_begin; + block->slab->offset += code_size; + + real_size = block->real_end - block->real_begin; + block->real_snapshot = block->code_end; + memcpy (block->real_snapshot, block->real_begin, real_size); + block->slab->offset += real_size; + + gum_stalker_freeze (block->ctx->stalker, block->code_begin, + code_size); +} +{% endhighlight %} + +现在让我们回到函数 `gum_exec_ctx_obtain_block_for()` 的更多细节。 +首先我们应该注意每个块都有一条指令作为前缀。 + +{% highlight c %} +gum_arm64_writer_put_ldp_reg_reg_reg_offset (cw, ARM64_REG_X16, + ARM64_REG_X17, ARM64_REG_SP, 16 + GUM_RED_ZONE_SIZE, + GUM_INDEX_POST_ADJUST); +{% endhighlight %} + +此指令是恢复序言(由 `GUM_RESTORATION_PROLOG_SIZE` 表示)。 +这在"引导"使用中被跳过——因此你会注意到当返回插桩代码的地址时, +`_gum_stalker_do_follow_me()` 和 `gum_stalker_infect()` 会添加此常量。 +但是,当返回指令被插桩时,如果返回到已经插桩的块,那么我们可以简单地返回到该块, +而不是返回到 Stalker 引擎。此代码由 `gum_exec_block_write_ret_transfer_code()` 编写。 +在最坏的情况下,我们可能需要使用寄存器来执行到插桩块的最终分支, +此函数将它们存储到堆栈中,并且从堆栈恢复这些的代码在块本身中作为前缀。 +因此,如果我们可以直接返回到插桩块,我们将返回到此第一条指令,而不是跳过 `GUM_RESTORATION_PROLOG_SIZE` 字节。 + +其次,我们可以看到 `gum_exec_ctx_obtain_block_for()` 在插桩块写入后执行以下操作: + +{% highlight c %} +gum_arm64_writer_put_brk_imm (cw, 14); +{% endhighlight %} + +这插入一个断点指令,旨在简化调试。 + +最后,如果配置了 Stalker,`gum_exec_ctx_obtain_block_for()` 将在编译块时生成类型为 `GUM_COMPILE` 的事件。 + +## Helpers + +我们可以从 `gum_exec_ctx_ensure_inline_helpers_reachable()` 看到我们总共有 6 个 helpers。 +这些 helpers 是我们的插桩块重复需要的常见代码片段。我们不是重复发出它们包含的代码, +而是写一次并放置调用或分支指令让我们的插桩代码执行它。 +回想一下,helpers 被写入我们正在写入插桩代码的相同 slabs 中, +如果可能,我们可以重用写入先前附近 slab 的 helper,而不是在每个 slab 中放置副本。 + +此函数为每个 helper 调用 `gum_exec_ctx_ensure_helper_reachable()`, +后者又调用 `gum_exec_ctx_is_helper_reachable()` 来检查 helper 是否在范围内, +或者调用作为第二个参数传递的回调来写出新副本。 + +{% highlight c %} +static void +gum_exec_ctx_ensure_inline_helpers_reachable (GumExecCtx * ctx) +{ + gum_exec_ctx_ensure_helper_reachable (ctx, + &ctx->last_prolog_minimal, + gum_exec_ctx_write_minimal_prolog_helper); + + gum_exec_ctx_ensure_helper_reachable (ctx, + &ctx->last_epilog_minimal, + gum_exec_ctx_write_minimal_epilog_helper); + + gum_exec_ctx_ensure_helper_reachable (ctx, + &ctx->last_prolog_full, + gum_exec_ctx_write_full_prolog_helper); + + gum_exec_ctx_ensure_helper_reachable (ctx, + &ctx->last_epilog_full, + gum_exec_ctx_write_full_epilog_helper); + + gum_exec_ctx_ensure_helper_reachable (ctx, + &ctx->last_stack_push, + gum_exec_ctx_write_stack_push_helper); + + gum_exec_ctx_ensure_helper_reachable (ctx, + &ctx->last_stack_pop_and_go, + gum_exec_ctx_write_stack_pop_and_go_helper); +} +{% endhighlight %} + +那么,我们的 6 个 helpers 是什么。我们有 2 个用于编写存储寄存器上下文的序言, +一个用于完整上下文,一个用于最小上下文。我们稍后会介绍这些。 +我们还有 2 个用于恢复寄存器的相应尾声。另外两个,`last_stack_push` 和 `last_stack_pop_and_go` 在插桩调用指令时使用。 + +在详细分析这两个之前,我们首先需要了解帧结构。我们可以从下面的代码片段中看到, +我们分配一个页面来包含 `GumExecFrame` 结构。这些结构按顺序存储在页面中,就像一个数组, +并从页面末尾的条目开始填充。每个帧包含原始块的地址和我们生成的用于替换它的插桩块的地址: + +{% highlight c %} +typedef struct _GumExecFrame GumExecFrame; +typedef struct _GumExecCtx GumExecCtx; + +struct _GumExecFrame +{ + gpointer real_address; + gpointer code_address; +}; + +struct _GumExecCtx +{ + ... + GumExecFrame * current_frame; + GumExecFrame * first_frame; + GumExecFrame * frames; + ... +}; + +static GumExecCtx * +gum_stalker_create_exec_ctx (GumStalker * self, + GumThreadId thread_id, + GumStalkerTransformer * transformer, + GumEventSink * sink) +{ + ... + + ctx->frames = gum_memory_allocate ( + NULL, self->page_size, self->page_size, GUM_PAGE_RW); + ctx->first_frame = (GumExecFrame *) ((guint8 *) ctx->frames + + self->page_size - sizeof (GumExecFrame)); + ctx->current_frame = ctx->first_frame; + + ... + + return ctx; +} +{% endhighlight %} + +### last_stack_push + +理解 Stalker 和特别是 helpers 的大部分复杂性在于, +一些函数——让我们称它们为写入器——编写稍后执行的代码。 +这些写入器本身有分支,确定要编写的确切代码,并且编写的代码有时也可以有分支。 +因此,我对这两个 helpers 采取的方法是显示将发出到 slab 中的汇编的伪代码, +该代码将由插桩块调用。 + +此 helper 的伪代码如下所示: + +{% highlight c %} +void +last_stack_push_helper (gpointer x0, + gpointer x1) +{ + GumExecFrame ** x16 = &ctx->current_frame + GumExecFrame * x17 = *x16 + gpointer x2 = x17 & (ctx->stalker->page_size - 1) + if x2 != 0: + x17-- + x17->real_address = x0 + x17->code_address = x1 + *x16 = x17 + return +} +{% endhighlight %} + +正如我们所看到的,这个 helper 实际上是一个简单的函数,它接受两个参数, +要存储在下一个 `GumExecFrame` 结构中的 `real_address` 和 `code_address`。 +请注意,我们的堆栈从它们存储的页面末尾向开始向后写入, +并且 `current_frame` 指向最后使用的条目(所以我们的堆栈是满的和递减的)。 +还要注意,我们有一个条件检查来查看我们是否在最后一个条目上 +(页面最开始的那个将是页面对齐的),如果我们用完了更多条目的空间(我们有 512 个空间), +那么我们什么也不做。如果我们有空间,我们将参数中的值写入条目, +并延迟 `current_frame` 指针以指向它。 + +此 helper 在*虚拟化*调用指令时使用。虚拟化是给替换指令(通常是与分支相关的指令) +的名称,用一系列指令代替执行预期块,允许 Stalker 管理控制流。 +回想一下,当我们的转换器使用迭代器遍历指令并调用 `iterator.keep()` 时, +我们输出转换后的指令。当我们遇到分支时,我们需要发出代码以回调到 Stalker 引擎, +以便它可以插桩该块,但如果分支语句是调用指令(`BL`、`BLX` 等), +我们还需要发出对上述 helper 的调用以存储堆栈帧信息。 +此信息在发出调用事件时使用,以及稍后在优化返回时使用。 + +### last_stack_pop_and_go + +现在让我们看看 `last_stack_pop_and_go` helper。要理解这一点, +我们还需要了解 `gum_exec_block_write_ret_transfer_code()`(调用它的代码)编写的代码, +以及它调用的 `gum_exec_block_write_exec_generated_code()` 编写的代码。 +我们现在将跳过指针认证。 + +{% highlight c %} +void +ret_transfer_code (arm64_reg ret_reg) +{ + gpointer x16 = ret_reg + goto last_stack_pop_and_go_helper +} + +void +last_stack_pop_and_go_helper (gpointer x16) +{ + GumExecFrame ** x0 = &ctx->current_frame + GumExecFrame * x1 = *x0 + gpointer x17 = x0.real_address + if x17 == x16: + x17 = x0->code_address + x1++ + *x0 = x1 + goto x17 + else: + x1 = ctx->first_frame + *x0 = x1 + gpointer * x0 = &ctx->return_at + *x0 = x16 + last_prologue_minimal() + x0 = &ctx->return_at + x1 = *x0 + gum_exec_ctx_replace_current_block_from_ret(ctx, x1) + last_epilogue_minimal() + goto exec_generated_code +} + +void +exec_generated_code (void) +{ + gpointer * x16 = &ctx->resume_at + gpointer x17 = *x16 + goto x17 +} +{% endhighlight %} + +所以这段代码有点难。它实际上不是一个函数,实际的汇编由于需要保存和恢复寄存器而有点混乱。 +但它的本质是:当虚拟化返回指令时,此 helper 用于优化将控制权传递回调用者。 +ret_reg 包含我们打算返回到的块的地址。 + +让我们看看返回指令的定义: + +> RET +> 从子例程返回,无条件分支到寄存器中的地址, +> 并提示这是子例程返回。 +> +> RET {Xn} +> 其中: +> +> Xn +> 是保存要分支到的地址的通用寄存器的 64 位名称, +> 范围为 0 到 31。如果不存在,则默认为 X30。 + +正如我们所看到的,我们将返回到寄存器中传递的地址。 +通常,我们可以预测寄存器值以及我们将返回到哪里, +因为编译器将发出汇编代码,以便将寄存器设置为紧跟在将我们带到那里的调用之后的指令的地址。 +在发出插桩调用后,我们直接在其后发出一个小着陆垫,它将回调到 Stalker 以插桩下一个块。 +稍后可以回填此着陆垫(如果条件合适)以避免完全重新进入 Stalker。 +我们将原始块的地址和调用后的此着陆垫存储在 `GumExecFrame` 结构中, +因此我们可以简单地通过用简单分支到此着陆垫的指令替换返回指令来虚拟化我们的返回指令。 +我们不需要每次看到返回指令时都重新进入 Stalker 引擎,并获得不错的性能提升。简单! + +但是,我们必须记住,并非所有调用都会导致返回。 +敌对或专用代码的常见技术是进行调用以使用 `LR` 来确定指令指针的当前位置。 +然后可以将此值用于内省目的(例如验证代码以检测修改、解密或解扰代码等)。 + +此外,请记住用户可以使用自定义转换来随意修改指令, +他们可以插入修改寄存器值的指令,或者可能是传递上下文结构的 callout 函数, +允许他们随意修改寄存器值。现在考虑如果他们修改返回寄存器中的值会怎样! + +所以我们可以看到 helper 检查返回寄存器的值与 `GumExecFrame` 中存储的 `real_address` 的值。 +如果匹配,那么一切都很好,我们可以简单地直接分支回着陆垫。 +回想一下,在第一个实例中,这只是重新进入 Stalker 以插桩下一个块并分支到它, +但在稍后的时间点,可以使用回填直接分支到此插桩块并避免完全重新进入 Stalker。 + +否则,我们遵循不同的路径。首先清除 `GumExecFrame` 数组, +现在我们的控制流已经偏离,我们将再次开始构建我们的堆栈。 +我们接受,如果我们曾经返回到它们,我们将为到目前为止记录的调用堆栈中的任何先前帧采取相同的较慢路径, +但将有可能为我们从这里遇到的新调用使用快速路径(直到下次以非常规方式使用调用指令)。 + +我们制作一个最小序言(我们的插桩代码现在将不得不重新进入 Stalker), +我们需要能够在将控制权返回给它之前恢复应用程序的寄存器。 +我们调用返回的入口门 `gum_exec_ctx_replace_current_block_from_ret()`(稍后会详细介绍入口门)。 +然后我们在分支到 `ctx->resume_at` 指针之前执行相应的尾声, +该指针在上述对 `gum_exec_ctx_replace_current_block_from_ret()` 的调用期间由 Stalker 设置为指向新的插桩块。 +## 上下文 + +现在让我们看看序言和尾声。 + +{% highlight c %} +static void +gum_exec_ctx_write_prolog (GumExecCtx * ctx, + GumPrologType type, + GumArm64Writer * cw) +{ + gpointer helper; + + helper = (type == GUM_PROLOG_MINIMAL) + ? ctx->last_prolog_minimal + : ctx->last_prolog_full; + + gum_arm64_writer_put_stp_reg_reg_reg_offset (cw, ARM64_REG_X19, + ARM64_REG_LR, ARM64_REG_SP, -(16 + GUM_RED_ZONE_SIZE), + GUM_INDEX_PRE_ADJUST); + gum_arm64_writer_put_bl_imm (cw, GUM_ADDRESS (helper)); +} + +static void +gum_exec_ctx_write_epilog (GumExecCtx * ctx, + GumPrologType type, + GumArm64Writer * cw) +{ + gpointer helper; + + helper = (type == GUM_PROLOG_MINIMAL) + ? ctx->last_epilog_minimal + : ctx->last_epilog_full; + + gum_arm64_writer_put_bl_imm (cw, GUM_ADDRESS (helper)); + gum_arm64_writer_put_ldp_reg_reg_reg_offset (cw, ARM64_REG_X19, + ARM64_REG_X20, ARM64_REG_SP, 16 + GUM_RED_ZONE_SIZE, + GUM_INDEX_POST_ADJUST); +} +{% endhighlight %} + +我们可以看到,除了调用相应的序言或尾声 helpers 之外,这些并没有做太多事情。 +我们可以看到序言将 `X19` 和链接寄存器存储到堆栈中。 +然后在尾声结束时将这些恢复到 `X19` 和 `X20` 中。 +这是因为 `X19` 需要作为暂存空间来写入上下文块,并且需要保留链接寄存器,因为它会被 helper 调用破坏。 + +LDP 和 STP 指令分别加载和存储一对寄存器,并可以选择增加或减少堆栈指针。 +这种增加或减少可以在值加载或存储之前或之后进行。 + +还要注意放置这些寄存器的偏移量。它们存储在堆栈顶部之外的 `16` 字节 + `GUM_RED_ZONE_SIZE` 处。 +请注意,我们在 AArch64 上的堆栈是满的和递减的。这意味着堆栈向较低地址增长, +堆栈指针指向最后推入的项目(而不是下一个空白空间)。 +因此,如果我们从堆栈指针中减去 16 个字节,那么这给了我们足够的空间来存储两个 64 位寄存器。 +请注意,堆栈指针必须在存储之前递减(预递减)并在加载之后递增(后递增)。 + +那么 `GUM_RED_ZONE_SIZE` 是什么? +[redzone](http://hungri-yeti.com/2015/10/19/the-arm64-aarch64-stack/) 是堆栈指针之外的 128 字节区域, +函数可以使用它来存储临时变量。这允许函数在堆栈中存储数据,而无需一直调整堆栈指针。 +请注意,对序言的此调用可能是我们插桩块中执行的第一件事, +我们不知道应用程序代码在 redzone 中存储了什么局部变量, +因此在开始使用堆栈存储 Stalker 引擎的信息之前,我们必须确保将堆栈指针推进到它之外。 + +## 上下文 Helpers + +既然我们已经了解了如何调用这些 helpers,现在让我们看看 helpers 本身。 +虽然有两个序言和两个尾声(完整和最小),但它们都由同一个函数编写,因为它们有很多共同点。 +编写的版本基于函数参数。展示这些最简单的方法是使用带注释的代码: + +{% highlight c %} +static void +gum_exec_ctx_write_prolog_helper (GumExecCtx * ctx, + GumPrologType type, + GumArm64Writer * cw) +{ + // 跟踪我们推送到堆栈上的内容,因为我们将要在执行上下文中存储原始应用程序堆栈的位置。 + // 目前对我们 helper 的调用已经跳过了 red zone 并存储了 LR 和 X19。 + gint immediate_for_sp = 16 + GUM_RED_ZONE_SIZE; + + // 此指令用于将 CPU 标志存储到 X15 中。 + const guint32 mrs_x15_nzcv = 0xd53b420f; + + // 请注意,只有完整的序言必须看起来像 C 结构定义, + // 因为这是传递给 callouts 等的数据结构。 + + // 将返回地址保存到 X19 中的插桩块。我们将全程保留它,并在最后分支回那里。 + // 这将带我们回到由 gum_exec_ctx_write_prolog() 编写的代码 + gum_arm64_writer_put_mov_reg_reg (cw, ARM64_REG_X19, ARM64_REG_LR); + + // LR = SP[8] 将前一个块(或用户代码)的返回地址保存在 LR 中。 + // 这是由 gum_exec_ctx_write_prolog() 编写的代码推送到那里的。 + // 一旦我们返回到我们的插桩代码块,这就是将保留在 LR 中的那个。 + // 注意 SP+8 的使用在入口(序言)上有点不对称,因为它用于传递 LR。 + // 在出口(尾声)上,它用于传递 X20,因此 gum_exec_ctx_write_epilog() 在那里恢复它。 + gum_arm64_writer_put_ldr_reg_reg_offset (cw, + ARM64_REG_LR, ARM64_REG_SP, 8); + + // 存储 SP[8] = X20。我们已经读取了由 gum_exec_ctx_write_prolog() 放在那里的 LR 的值, + // 并正在那里写入 X20,以便它可以由 gum_exec_ctx_write_epilog() 编写的代码恢复 + gum_arm64_writer_put_str_reg_reg_offset (cw, + ARM64_REG_X20, ARM64_REG_SP, 8); + + if (type == GUM_PROLOG_MINIMAL) + { + // 存储所有 FP/NEON 寄存器。NEON 是 ARM 核心上的 SIMD 引擎, + // 允许一次对多个输入执行操作。 + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_Q6, ARM64_REG_Q7); + + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_Q4, ARM64_REG_Q5); + + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_Q2, ARM64_REG_Q3); + + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_Q0, ARM64_REG_Q1); + + immediate_for_sp += 4 * 32; + + // X29 是帧指针 + // X30 是链接寄存器 + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X29, ARM64_REG_X30); + + // 我们在这里使用 STP 推送成对的寄存器。实际上我们要推送奇数个, + // 所以我们只是推送 STALKER_REG_CTX 作为填充来凑数 + /* X19 - X28 是被调用者保存的寄存器 */ + + // 如果我们只调用编译的 C 代码,那么编译器将确保如果函数使用寄存器 X19 到 X28, + // 那么它们的值将被保留。因此,我们不需要在这里存储它们,因为它们不会被修改。 + // 但是,如果我们进行 callout,那么我们希望 Stalker 最终用户能够看到完整的寄存器集, + // 并能够对它们进行任何他们认为合适的修改。 + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X18, ARM64_REG_X30); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X16, ARM64_REG_X17); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X14, ARM64_REG_X15); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X12, ARM64_REG_X13); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X10, ARM64_REG_X11); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X8, ARM64_REG_X9); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X6, ARM64_REG_X7); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X4, ARM64_REG_X5); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X2, ARM64_REG_X3); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X0, ARM64_REG_X1); + immediate_for_sp += 11 * 16; + } + else if (type == GUM_PROLOG_FULL) + { + /* GumCpuContext.q[128] */ + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_Q6, ARM64_REG_Q7); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_Q4, ARM64_REG_Q5); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_Q2, ARM64_REG_Q3); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_Q0, ARM64_REG_Q1); + + /* GumCpuContext.x[29] + fp + lr + padding */ + // X29 是帧指针 + // X30 是链接寄存器 + // X15 再次被推送仅用于填充 + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X30, ARM64_REG_X15); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X28, ARM64_REG_X29); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X26, ARM64_REG_X27); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X24, ARM64_REG_X25); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X22, ARM64_REG_X23); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X20, ARM64_REG_X21); + + // 将 X19(当前持有此函数要返回的 LR 值,即由 gum_exec_ctx_write_prolog() 编写的调用者的地址) + // 暂时存储在 X20 中。我们已经推送了 X20,所以我们可以自由使用它, + // 但我们想将应用程序的 X19 值推入上下文。 + // 这是由 gum_exec_ctx_write_prolog() 中的代码推送到堆栈上的, + // 所以我们可以在推送之前从那里恢复它。 + gum_arm64_writer_put_mov_reg_reg (cw, + ARM64_REG_X20, ARM64_REG_X19); + + // 在调用 helper 之前,从序言推送的值恢复 X19。 + gum_arm64_writer_put_ldr_reg_reg_offset (cw, + ARM64_REG_X19, ARM64_REG_SP, + (6 * 16) + (4 * 32)); + + // 推送应用程序的 X18 和 X19 值。X18 未修改。我们上面已经更正了 X19。 + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X18, ARM64_REG_X19); + + // 从 X20 恢复 X19 + gum_arm64_writer_put_mov_reg_reg (cw, + ARM64_REG_X19, ARM64_REG_X20); + + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X16, ARM64_REG_X17); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X14, ARM64_REG_X15); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X12, ARM64_REG_X13); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X10, ARM64_REG_X11); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X8, ARM64_REG_X9); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X6, ARM64_REG_X7); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X4, ARM64_REG_X5); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X2, ARM64_REG_X3); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X0, ARM64_REG_X1); + + /* GumCpuContext.pc + sp */ + + // 我们将在这里存储 PC 和 SP。PC 设置为零,对于 SP, + // 我们必须在存储所有这些上下文信息之前计算原始 SP。 + // 注意我们在这里使用零寄存器(AArch64 中的一个特殊寄存器,总是具有值 0)。 + gum_arm64_writer_put_mov_reg_reg (cw, + ARM64_REG_X0, ARM64_REG_XZR); + gum_arm64_writer_put_add_reg_reg_imm (cw, + ARM64_REG_X1, ARM64_REG_SP, + (16 * 16) + (4 * 32) + 16 + GUM_RED_ZONE_SIZE); + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X0, ARM64_REG_X1); + + immediate_for_sp += sizeof (GumCpuContext) + 8; + } + + // 将算术逻辑单元标志存储到 X15 中。虽然看起来上面用于计算原始堆栈指针的 add 指令可能已经更改了标志, + // 但 AArch64 有一个不修改条件标志的 ADD 指令,以及一个修改条件标志的 ADDS 指令。 + gum_arm64_writer_put_instruction (cw, mrs_x15_nzcv); + + /* 方便地将 X20 指向保存的寄存器的开头 */ + // X20 稍后由诸如 gum_exec_ctx_load_real_register_from_full_frame_into() 之类的函数使用, + // 以发出引用保存帧的代码。 + gum_arm64_writer_put_mov_reg_reg (cw, ARM64_REG_X20, ARM64_REG_SP); + + /* padding + status */ + // 这会推送标志以确保在 Stalker 内部执行后可以正确恢复它们。 + gum_arm64_writer_put_push_reg_reg (cw, + ARM64_REG_X14, ARM64_REG_X15); + immediate_for_sp += 1 * 16; + + // 我们在入口处将 LR 保存到 X19 中,以便在此 helper 运行后我们可以分支回插桩代码。 + // 虽然插桩代码调用了我们,但在调用 helper 之前,我们将 LR 恢复到了其先前的值(应用程序代码)。 + // 虽然 LR 不是被调用者保存的(例如,在返回时保存和恢复它不是我们的责任,而是我们调用者的责任), + // 但在这里这样做是为了最小化插桩块中内联存根的代码大小。 + gum_arm64_writer_put_br_reg_no_auth (cw, ARM64_REG_X19); +} +{% endhighlight %} + +现在让我们看看尾声: + +{% highlight c %} +static void +gum_exec_ctx_write_epilog_helper (GumExecCtx * ctx, + GumPrologType type, + GumArm64Writer * cw) +{ + // 此指令用于将 X15 的值恢复回 ALU 标志。 + const guint32 msr_nzcv_x15 = 0xd51b420f; + + /* padding + status */ + // 注意我们还没有恢复标志,因为我们必须等到完成所有可能修改标志的操作(例如加法、减法等)。 + // 但是,我们必须在将 X15 恢复回其原始值之前这样做。 + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X14, ARM64_REG_X15); + + if (type == GUM_PROLOG_MINIMAL) + { + // 将 LR 保存在 X19 中,以便我们可以返回到插桩块中的调用者。 + // 请注意,我们必须在返回之前将链接寄存器 X30 恢复回其原始值(应用程序代码中的块)。 + // 这在下面执行。回想一下,我们的 X19 值由内联序言本身保存到堆栈中, + // 并由我们要返回的内联序言恢复。所以我们可以继续在这里将其用作暂存空间。 + gum_arm64_writer_put_mov_reg_reg (cw, + ARM64_REG_X19, ARM64_REG_LR); + + /* restore status */ + // 我们已经完成了所有可能改变标志的指令。 + gum_arm64_writer_put_instruction (cw, msr_nzcv_x15); + + // 恢复我们在上下文中保存的所有寄存器。我们早些时候推送了 X30 作为填充, + // 但我们将在紧接着弹出 X30 的实际推送值之前将其弹回那里。 + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X0, ARM64_REG_X1); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X2, ARM64_REG_X3); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X4, ARM64_REG_X5); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X6, ARM64_REG_X7); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X8, ARM64_REG_X9); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X10, ARM64_REG_X11); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X12, ARM64_REG_X13); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X14, ARM64_REG_X15); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X16, ARM64_REG_X17); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X18, ARM64_REG_X30); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X29, ARM64_REG_X30); + + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_Q0, ARM64_REG_Q1); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_Q2, ARM64_REG_Q3); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_Q4, ARM64_REG_Q5); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_Q6, ARM64_REG_Q7); + } + else if (type == GUM_PROLOG_FULL) + { + /* GumCpuContext.pc + sp */ + // 我们将堆栈指针和 PC 存储在堆栈中,但我们不想将 PC 恢复回用户代码, + // 并且我们的堆栈指针应该自然恢复,因为推送到其上的所有数据都被弹回。 + gum_arm64_writer_put_add_reg_reg_imm (cw, + ARM64_REG_SP, ARM64_REG_SP, 16); + + /* restore status */ + // 同样,既然上述加法已经完成,我们已经完成了任何影响标志的操作。 + gum_arm64_writer_put_instruction (cw, msr_nzcv_x15); + + /* GumCpuContext.x[29] + fp + lr + padding */ + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X0, ARM64_REG_X1); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X2, ARM64_REG_X3); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X4, ARM64_REG_X5); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X6, ARM64_REG_X7); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X8, ARM64_REG_X9); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X10, ARM64_REG_X11); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X12, ARM64_REG_X13); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X14, ARM64_REG_X15); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X16, ARM64_REG_X17); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X18, ARM64_REG_X19); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X20, ARM64_REG_X21); + + // 回想一下,X19 和 X20 实际上是由尾声本身恢复的, + // 因为 X19 在序言/尾声 helpers 期间用作暂存空间,而 X20 被序言重新用作指向上下文结构的指针。 + // 如果我们有一个完整的序言,那么这意味着它是为了我们可以进入一个 callout, + // 允许 Stalker 最终用户检查和修改所有寄存器。这意味着对上面上下文结构中寄存器的任何更改必须在运行时反映出来。 + // 因此,由于这些值是由尾声从堆栈的更高处恢复的,我们必须用上下文结构中的值覆盖那里的值。 + gum_arm64_writer_put_stp_reg_reg_reg_offset (cw, ARM64_REG_X19, + ARM64_REG_X20, ARM64_REG_SP, (5 * 16) + (4 * 32), + GUM_INDEX_SIGNED_OFFSET); + + // 将 LR 保存在 X19 中,以便我们可以返回到插桩代码中的调用者。 + // 请注意,我们必须在返回之前将链接寄存器 X30 恢复回其原始值。 + // 这在下面执行。回想一下,我们的 X19 值由内联序言本身保存到堆栈中, + // 并由我们要返回的内联尾声恢复。 + gum_arm64_writer_put_mov_reg_reg (cw, + ARM64_REG_X19, ARM64_REG_LR); + + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X22, ARM64_REG_X23); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X24, ARM64_REG_X25); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X26, ARM64_REG_X27); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X28, ARM64_REG_X29); + + // 回想一下,在构建序言时,X15 也作为填充与 X30 一起被推送。 + // 但是,Stalker 最终用户可以修改上下文,从而修改 X15 的值。 + // 然而,这不会影响作为填充存储在这里的重复项,因此 X15 将被破坏。 + // 因此,在从堆栈恢复两个寄存器之前,我们将现在恢复的 X15 值复制到存储此副本以进行填充的位置。 + gum_arm64_writer_put_str_reg_reg_offset (cw, + ARM64_REG_X15, ARM64_REG_SP, 8); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_X30, ARM64_REG_X15); + + /* GumCpuContext.q[128] */ + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_Q0, ARM64_REG_Q1); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_Q2, ARM64_REG_Q3); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_Q4, ARM64_REG_Q5); + gum_arm64_writer_put_pop_reg_reg (cw, + ARM64_REG_Q6, ARM64_REG_Q7); + } + + // 现在我们可以返回到我们的调用者(尾声的内联部分),LR 仍然设置为应用程序代码的原始值。 + gum_arm64_writer_put_br_reg_no_auth (cw, ARM64_REG_X19) +} +{% endhighlight %} + +这都很复杂。部分原因是我们只有一个寄存器用作暂存空间, +部分原因是我们希望将存储在插桩块中的内联序言和尾声代码保持在最低限度, +部分原因是因为我们的上下文值可以被 callouts 等更改。但希望现在这一切都有意义。 + +## 读取/写入上下文 + +既然我们已经保存了上下文,无论是完整上下文还是最小上下文, +Stalker 可能需要从上下文中读取寄存器以查看应用程序代码的状态。 +例如,查找分支或返回指令将要分支到的地址,以便我们可以插桩该块。 + +当 Stalker 编写序言和尾声代码时,它通过调用 `gum_exec_block_open_prolog()` 和 `gum_exec_block_close_prolog()` 来完成。 +这些将已编写的序言类型存储在 `gc->opened_prolog` 中。 + +{% highlight c %} +static void +gum_exec_block_open_prolog (GumExecBlock * block, + GumPrologType type, + GumGeneratorContext * gc) +{ + if (gc->opened_prolog >= type) + return; + + /* 出于性能原因,我们不想处理这种情况 */ + g_assert (gc->opened_prolog == GUM_PROLOG_NONE); + + gc->opened_prolog = type; + + gum_exec_ctx_write_prolog (block->ctx, type, gc->code_writer); +} + +static void +gum_exec_block_close_prolog (GumExecBlock * block, + GumGeneratorContext * gc) +{ + if (gc->opened_prolog == GUM_PROLOG_NONE) + return; + + gum_exec_ctx_write_epilog (block->ctx, gc->opened_prolog, + gc->code_writer); + + gc->opened_prolog = GUM_PROLOG_NONE; +} +{% endhighlight %} + +因此,当我们想要读取寄存器时,这可以通过单个函数 `gum_exec_ctx_load_real_register_into()` 来实现。 +这确定正在使用哪种序言并相应地调用相关例程。请注意,这些例程实际上并不读取寄存器,它们发出读取它们的代码。 + +{% highlight c %} +static void +gum_exec_ctx_load_real_register_into (GumExecCtx * ctx, + arm64_reg target_register, + arm64_reg source_register, + GumGeneratorContext * gc) +{ + if (gc->opened_prolog == GUM_PROLOG_MINIMAL) + { + gum_exec_ctx_load_real_register_from_minimal_frame_into (ctx, + target_register, source_register, gc); + return; + } + else if (gc->opened_prolog == GUM_PROLOG_FULL) + { + gum_exec_ctx_load_real_register_from_full_frame_into (ctx, + target_register, source_register, gc); + return; + } + + g_assert_not_reached (); +} +{% endhighlight %} + +从完整帧读取寄存器实际上是最简单的。我们可以看到代码与用于将上下文传递给 callouts 等的结构紧密匹配。 +请记住,在每种情况下,寄存器 `X20` 都指向上下文结构的基址。 + +{% highlight c %} +typedef GumArm64CpuContext GumCpuContext; + +struct _GumArm64CpuContext +{ + guint64 pc; + guint64 sp; + + guint64 x[29]; + guint64 fp; + guint64 lr; + guint8 q[128]; +}; + +static void +gum_exec_ctx_load_real_register_from_full_frame_into ( + GumExecCtx * ctx, + arm64_reg target_register, + arm64_reg source_register, + GumGeneratorContext * gc) +{ + GumArm64Writer * cw; + + cw = gc->code_writer; + + if (source_register >= ARM64_REG_X0 && + source_register <= ARM64_REG_X28) + { + gum_arm64_writer_put_ldr_reg_reg_offset (cw, + target_register, ARM64_REG_X20, + G_STRUCT_OFFSET (GumCpuContext, x) + + ((source_register - ARM64_REG_X0) * 8)); + } + else if (source_register == ARM64_REG_X29) + { + gum_arm64_writer_put_ldr_reg_reg_offset (cw, + target_register, ARM64_REG_X20, + G_STRUCT_OFFSET (GumCpuContext, fp)); + } + else if (source_register == ARM64_REG_X30) + { + gum_arm64_writer_put_ldr_reg_reg_offset (cw, + target_register, ARM64_REG_X20, + G_STRUCT_OFFSET (GumCpuContext, lr)); + } + else + { + gum_arm64_writer_put_mov_reg_reg (cw, + target_register, source_register); + } +} +{% endhighlight %} + +从最小上下文读取实际上有点难。`X0` 到 `X18` 很简单,它们存储在上下文块中。 +`X18` 之后是 8 字节填充(总共 10 对寄存器),然后是 `X29` 和 `X30`。这总共有 11 对寄存器。 +紧随其后的是 NEON/浮点寄存器(总共 128 字节)。最后 `X19` 和 `X20` 存储在此之上, +因为它们由 `gum_exec_ctx_write_epilog()` 编写的内联尾声代码恢复。 + +{% highlight c %} +static void +gum_exec_ctx_load_real_register_from_minimal_frame_into ( + GumExecCtx * ctx, + arm64_reg target_register, + arm64_reg source_register, + GumGeneratorContext * gc) +{ + GumArm64Writer * cw; + + cw = gc->code_writer; + + if (source_register >= ARM64_REG_X0 && + source_register <= ARM64_REG_X18) + { + gum_arm64_writer_put_ldr_reg_reg_offset (cw, + target_register, ARM64_REG_X20, + (source_register - ARM64_REG_X0) * 8); + } + else if (source_register == ARM64_REG_X19 || + source_register == ARM64_REG_X20) + { + gum_arm64_writer_put_ldr_reg_reg_offset (cw, + target_register, ARM64_REG_X20, + (11 * 16) + (4 * 32) + + ((source_register - ARM64_REG_X19) * 8)); + } + else if (source_register == ARM64_REG_X29 || + source_register == ARM64_REG_X30) + { + gum_arm64_writer_put_ldr_reg_reg_offset (cw, + target_register, ARM64_REG_X20, + (10 * 16) + ((source_register - ARM64_REG_X29) * 8)); + } + else + { + gum_arm64_writer_put_mov_reg_reg (cw, + target_register, source_register); + } +} +{% endhighlight %} + +## 控制流 + +Stalker 的执行从 3 个入口点之一开始: + +* `_gum_stalker_do_follow_me()` +* `gum_stalker_infect()` +* `gum_exec_ctx_replace_current_block_with()` + +前两个我们已经介绍过了,它们初始化 Stalker 引擎并开始插桩第一个执行块。 +`gum_exec_ctx_replace_current_block_with()` 用于插桩后续块。 +事实上,此函数与前两个函数的主要区别在于 Stalker 引擎已经初始化,因此不需要重复此工作。 +这三个都调用 `gum_exec_ctx_obtain_block_for()` 来生成插桩块。 + +我们之前在转换器部分介绍了 `gum_exec_ctx_obtain_block_for()`。 +它调用正在使用的转换实现,默认情况下调用 `gum_stalker_iterator_next()`, +后者使用 `gum_arm64_relocator_read_one()` 调用重定位器以读取下一条重定位指令。 +然后它调用 `gum_stalker_iterator_keep()` 来生成插桩副本。 +它在一个循环中执行此操作,直到 `gum_stalker_iterator_next()` 返回 `FALSE`,因为它已到达块的末尾。 + +大多数时候 `gum_stalker_iterator_keep()` 将简单地调用 `gum_arm64_relocator_write_one()` 来按原样发出重定位指令。 +但是,如果指令是分支或返回指令,它将分别调用 `gum_exec_block_virtualize_branch_insn()` 或 `gum_exec_block_virtualize_ret_insn()`。 +我们将稍后更详细地介绍这两个虚拟化函数,它们发出代码以通过入口门将控制权转移回 `gum_exec_ctx_replace_current_block_with()`, +准备处理下一个块(除非有优化可以让我们绕过 Stalker 并直接进入下一个插桩块,或者我们正在进入排除范围)。 + +## Gates + +入口门(Entry gates)由宏生成,每个在块末尾找到的不同指令类型都有一个。 +当我们虚拟化每种类型的指令时,我们通过这些门之一将控制流引导回 `gum_exec_ctx_replace_current_block_with()` 函数。 +我们可以看到门的实现非常简单,它更新已被调用的次数计数器, +并将控制权传递给 `gum_exec_ctx_replace_current_block_with()`, +传递它被调用时的参数、`GumExecCtx` 和要插桩的下一个块的 `start_address`。 + +{% highlight c %} +static gboolean counters_enabled = FALSE; +static guint total_transitions = 0; + +#define GUM_ENTRYGATE(name) \ + gum_exec_ctx_replace_current_block_from_##name +#define GUM_DEFINE_ENTRYGATE(name) \ + static guint total_##name##s = 0; \ + \ + static gpointer GUM_THUNK \ + GUM_ENTRYGATE (name) ( \ + GumExecCtx * ctx, \ + gpointer start_address) \ + { \ + if (counters_enabled) \ + total_##name##s++; \ + \ + return gum_exec_ctx_replace_current_block_with (ctx, \ + start_address); \ + } +#define GUM_PRINT_ENTRYGATE_COUNTER(name) \ + g_printerr ("\t" G_STRINGIFY (name) "s: %u\n", total_##name##s) +{% endhighlight %} + +这些计数器可以通过以下例程显示。它们仅供测试套件使用,而不是通过 API 暴露给用户。 + +{% highlight c %} +#define GUM_PRINT_ENTRYGATE_COUNTER(name) \ + g_printerr ("\t" G_STRINGIFY (name) "s: %u\n", total_##name##s) + +void +gum_stalker_dump_counters (void) +{ + g_printerr ("\n\ntotal_transitions: %u\n", total_transitions); + + GUM_PRINT_ENTRYGATE_COUNTER (call_imm); + GUM_PRINT_ENTRYGATE_COUNTER (call_reg); + GUM_PRINT_ENTRYGATE_COUNTER (post_call_invoke); + GUM_PRINT_ENTRYGATE_COUNTER (excluded_call_imm); + GUM_PRINT_ENTRYGATE_COUNTER (excluded_call_reg); + GUM_PRINT_ENTRYGATE_COUNTER (ret); + + GUM_PRINT_ENTRYGATE_COUNTER (jmp_imm); + GUM_PRINT_ENTRYGATE_COUNTER (jmp_reg); + + GUM_PRINT_ENTRYGATE_COUNTER (jmp_cond_cc); + GUM_PRINT_ENTRYGATE_COUNTER (jmp_cond_cbz); + GUM_PRINT_ENTRYGATE_COUNTER (jmp_cond_cbnz); + GUM_PRINT_ENTRYGATE_COUNTER (jmp_cond_tbz); + GUM_PRINT_ENTRYGATE_COUNTER (jmp_cond_tbnz); + + GUM_PRINT_ENTRYGATE_COUNTER (jmp_continuation); +} +{% endhighlight %} + +## 虚拟化函数 + +现在让我们更详细地看看我们用于替换在每个块末尾找到的分支指令的*虚拟化*。 +我们有四个这样的函数: + +* `gum_exec_block_virtualize_branch_insn()` +* `gum_exec_block_virtualize_ret_insn()` +* `gum_exec_block_virtualize_sysenter_insn()` +* `gum_exec_block_virtualize_linux_sysenter()` + +我们可以看到其中两个与系统调用有关(实际上,一个调用另一个),我们稍后会介绍这些。 +让我们看看用于分支和返回的那些。 + +### gum_exec_block_virtualize_branch_insn + +此例程首先确定分支的目的地是来自指令中的立即偏移量还是寄存器。 +在后者的情况下,我们还不能提取值,我们只确定哪个寄存器。这被称为 `target`。 +函数的下一部分处理分支指令。这包括条件和非条件分支。 +对于条件目标,如果不采用分支,则目的地称为 `cond_target`,这设置为原始块中下一条指令的地址。 + +同样,`regular_entry_func` 和 `cond_entry_func` 用于保存将用于处理分支的入口门。 +前者用于保存用于非条件分支的门,`cond_entry_func` 保存用于条件分支的门(无论是否采用)。 + +函数 `gum_exec_block_write_jmp_transfer_code()` 用于编写分支到入口门所需的代码。 +对于非条件分支,这很简单,我们调用传递 `target` 和 `regular_entry_func` 的函数。 +对于条件分支,事情稍微复杂一些。我们的输出类似于以下伪代码: + +{% highlight c %} + INVERSE_OF_ORIGINAL_BRANCH(is_false) + jmp_transfer_code(target, cond_entry_func) +is_false: + jmp_transfer_code(cond_target, cond_entry_func) +{% endhighlight %} + +在这里,我们可以看到我们首先将分支指令写入我们的插桩块, +就像在我们的插桩块中一样,我们还需要确定是否应该采用分支。 +但我们不是直接分支到目标,就像对于非条件分支一样, +我们使用 `gum_exec_block_write_jmp_transfer_code()` 编写代码, +通过相关的入口门跳回 Stalker,传递我们要分支到的真实地址。 +但请注意,分支与原始分支反转(例如 `CBZ` 将被 `CBNZ` 替换)。 + +现在,让我们看看 `gum_exec_block_virtualize_branch_insn()` 如何处理调用。 +首先,如果我们配置为生成调用事件,我们会发出代码来生成调用事件。 +接下来我们检查是否有任何探针在使用中。如果有,那么我们调用 `gum_exec_block_write_call_probe_code()` +来发出调用任何注册的探针回调所需的代码。接下来,我们检查调用是否针对排除范围 +(注意我们只能在调用是针对立即地址时才能这样做),如果是,那么我们按原样发出指令。 +但是,我们随后使用 `gum_exec_block_write_jmp_transfer_code()`, +就像我们在处理分支时所做的那样,发出代码以在返回地址处回调到 Stalker 以插桩块。 +请注意,这里我们使用 `excluded_call_imm` 入口门。 + +最后,如果它只是一个普通的调用表达式,那么我们使用函数 `gum_exec_block_write_call_invoke_code()` 来发出处理调用的代码。 +由于所有回填优化,此函数相当复杂,因此我们只看基础知识。 + +还记得之前在 `gum_exec_block_virtualize_branch_insn()` 中, +我们只能在目标在立即数中指定时检查我们的调用是否针对排除范围吗? +好吧,如果目标是在寄存器中指定的,那么在这里我们发出代码来检查目标是否在排除范围内。 +这是通过使用 `gum_exec_ctx_write_push_branch_target_address()` 加载目标寄存器 +(这反过来调用我们之前介绍的 `gum_exec_ctx_load_real_register_into()` 来读取上下文) +并发出代码来调用 `gum_exec_block_check_address_for_exclusion()` 来完成的,其实现非常不言自明。 +如果它被排除,则采用分支,并使用与上面讨论的处理排除的立即调用时描述的类似代码。 + +接下来我们发出代码来调用入口门并生成被调用者的插桩块。 +然后调用 helper `last_stack_push` 将我们的 `GumExecFrame` 添加到我们的上下文中,其中包含原始和插桩块地址。 +真实和插桩代码地址分别从 GeneratorContext 和 CodeWriter 的当前光标位置读取, +然后我们为返回地址生成所需的着陆垫(这是我们之前介绍的优化, +我们可以在执行虚拟化返回语句时直接跳到此块,而不是重新进入 Stalker)。 +最后,我们使用 `gum_exec_block_write_exec_generated_code()` 发出代码以分支到插桩的被调用者。 + +### gum_exec_block_virtualize_ret_insn + +在查看了调用指令的虚拟化之后,你会很高兴知道这个相对简单! +如果配置了,此函数调用 `gum_exec_block_write_ret_event_code()` 来为返回语句生成事件。 +然后它调用 `gum_exec_block_write_ret_transfer_code()` 来生成处理返回指令所需的代码。 +这也太简单了,它发出代码来调用我们之前介绍的 `last_stack_pop_and_go` helper。 +## 发出事件 + +事件是 Stalker 引擎的关键输出之一。它们由以下函数发出。它们的实现同样非常不言自明: + +* `gum_exec_ctx_emit_call_event()` +* `gum_exec_ctx_emit_ret_event()` +* `gum_exec_ctx_emit_exec_event()` +* `gum_exec_ctx_emit_block_event()` + +然而,这些函数中每一个都需要注意的一点是,它们都调用 `gum_exec_block_write_unfollow_check_code()` +来生成检查 Stalker 是否要停止跟踪的代码。我们接下来将更详细地看看这个。 + +## 取消跟踪和清理 + +如果我们查看生成插桩代码以检查我们是否被要求取消跟踪的函数,我们可以看到它导致线程调用 +`gum_exec_ctx_maybe_unfollow()`,传递要插桩的下一条指令的地址。 +我们可以看到,如果状态已设置为停止跟踪,那么我们只需分支回原始代码。 + +{% highlight c %} +static void +gum_exec_block_write_unfollow_check_code (GumExecBlock * block, + GumGeneratorContext * gc, + GumCodeContext cc) +{ + GumExecCtx * ctx = block->ctx; + GumArm64Writer * cw = gc->code_writer; + gconstpointer beach = cw->code + 1; + GumPrologType opened_prolog; + + if (cc != GUM_CODE_INTERRUPTIBLE) + return; + + gum_arm64_writer_put_call_address_with_arguments (cw, + GUM_ADDRESS (gum_exec_ctx_maybe_unfollow), 2, + GUM_ARG_ADDRESS, GUM_ADDRESS (ctx), + GUM_ARG_ADDRESS, GUM_ADDRESS (gc->instruction->begin)); + gum_arm64_writer_put_cbz_reg_label (cw, ARM64_REG_X0, beach); + + opened_prolog = gc->opened_prolog; + gum_exec_block_close_prolog (block, gc); + gc->opened_prolog = opened_prolog; + + gum_arm64_writer_put_ldr_reg_address (cw, ARM64_REG_X16, + GUM_ADDRESS (&ctx->resume_at)); + gum_arm64_writer_put_ldr_reg_reg_offset (cw, + ARM64_REG_X17, ARM64_REG_X16, 0); + gum_arm64_writer_put_br_reg_no_auth (cw, ARM64_REG_X17); + + gum_arm64_writer_put_label (cw, beach); +} + +static gboolean +gum_exec_ctx_maybe_unfollow (GumExecCtx * ctx, + gpointer resume_at) +{ + if (g_atomic_int_get (&ctx->state) != + GUM_EXEC_CTX_UNFOLLOW_PENDING) + return FALSE; + + if (ctx->pending_calls > 0) + return FALSE; + + gum_exec_ctx_unfollow (ctx, resume_at); + + return TRUE; +} + +static void +gum_exec_ctx_unfollow (GumExecCtx * ctx, + gpointer resume_at) +{ + ctx->current_block = NULL; + + ctx->resume_at = resume_at; + + gum_tls_key_set_value (ctx->stalker->exec_ctx, NULL); + + ctx->destroy_pending_since = g_get_monotonic_time (); + g_atomic_int_set (&ctx->state, GUM_EXEC_CTX_DESTROY_PENDING); +} +{% endhighlight %} + +关于挂起调用的简要说明。如果我们调用排除范围,那么我们在插桩代码中发出原始调用,然后回调到 Stalker。 +然而,当线程在排除范围内运行时,我们无法控制指令指针,直到它返回。 +因此,我们只需要跟踪这些并等待线程退出排除范围。 + +现在我们可以看到运行线程如何优雅地回到运行正常的未插桩代码,让我们看看我们首先如何停止跟踪。 +我们有两种可能的方法来停止跟踪: + +* `gum_stalker_unfollow_me()` +* `gum_stalker_unfollow()` + +第一个很简单,我们将状态设置为停止跟踪。然后调用 `gum_exec_ctx_maybe_unfollow()` +尝试停止跟踪当前线程,然后处理 Stalker 上下文。 + +{% highlight c %} +void +gum_stalker_unfollow_me (GumStalker * self) +{ + GumExecCtx * ctx; + + ctx = gum_stalker_get_exec_ctx (self); + if (ctx == NULL) + return; + + g_atomic_int_set (&ctx->state, GUM_EXEC_CTX_UNFOLLOW_PENDING); + + if (!gum_exec_ctx_maybe_unfollow (ctx, NULL)) + return; + + g_assert (ctx->unfollow_called_while_still_following); + + gum_stalker_destroy_exec_ctx (self, ctx); +} +{% endhighlight %} + +我们在这里注意到我们将 `NULL` 作为地址传递给 `gum_exec_ctx_maybe_unfollow()`,这看起来可能很奇怪, +但我们可以看到在这种情况下它没有被使用,因为当我们插桩一个块时 +(记住 `gum_exec_ctx_replace_current_block_with()` 是入口门引导我们插桩后续块的地方) +我们检查是否即将调用 `gum_unfollow_me()`,如果是,那么我们从函数返回原始块, +而不是 `gum_exec_ctx_obtain_block_for()` 生成的插桩块的地址。 +因此我们可以看到这是一个特殊情况,此函数未被跟踪。我们只是跳到真实函数, +所以此时我们已经永远停止跟踪该线程。这种处理与排除范围不同, +因为对于那些范围,我们在插桩块中保留原始调用指令,但随后跟随回调到 Stalker。 +在这种情况下,我们只是跳转回原始未插桩块: + +{% highlight c %} +static gpointer gum_unfollow_me_address; + +static void +gum_stalker_class_init (GumStalkerClass * klass) +{ + ... + gum_unfollow_me_address = gum_strip_code_pointer ( + gum_stalker_unfollow_me); + ... +} + +static gpointer +gum_exec_ctx_replace_current_block_with (GumExecCtx * ctx, + gpointer start_address) +{ + ... + + if (start_address == gum_unfollow_me_address || + start_address == gum_deactivate_address) + { + ctx->unfollow_called_while_still_following = TRUE; + ctx->current_block = NULL; + ctx->resume_at = start_address; + } + ... + + else + { + ctx->current_block = gum_exec_ctx_obtain_block_for (ctx, + start_address, &ctx->resume_at); + + ... + } + + return ctx->resume_at; + + ... +} +{% endhighlight %} + +现在让我们看看 `gum_stalker_unfollow()`: + +{% highlight c %} +void +gum_stalker_unfollow (GumStalker * self, + GumThreadId thread_id) +{ + if (thread_id == gum_process_get_current_thread_id ()) + { + gum_stalker_unfollow_me (self); + } + else + { + GSList * cur; + + GUM_STALKER_LOCK (self); + + for (cur = self->contexts; cur != NULL; cur = cur->next) + { + GumExecCtx * ctx = (GumExecCtx *) cur->data; + + if (ctx->thread_id == thread_id && + g_atomic_int_compare_and_exchange (&ctx->state, + GUM_EXEC_CTX_ACTIVE, + GUM_EXEC_CTX_UNFOLLOW_PENDING)) + { + GUM_STALKER_UNLOCK (self); + + if (!gum_exec_ctx_has_executed (ctx)) + { + GumDisinfectContext dc; + + dc.exec_ctx = ctx; + dc.success = FALSE; + + gum_process_modify_thread (thread_id, + gum_stalker_disinfect, &dc); + + if (dc.success) + gum_stalker_destroy_exec_ctx (self, ctx); + } + + return; + } + } + + GUM_STALKER_UNLOCK (self); + } +} +{% endhighlight %} + +此函数查看上下文列表,查找请求线程的上下文。再次,它将上下文的状态设置为 `GUM_EXEC_CTX_UNFOLLOW_PENDING`。 +如果线程已经运行,我们必须等待它检查此标志并返回正常执行。 +但是,如果它尚未运行(也许当我们要求跟踪它时它处于阻塞系统调用中,并且从未在第一时间被感染), +那么我们可以通过调用 `gum_process_modify_thread()` 来修改线程上下文(此函数前面已详细描述) +并使用 `gum_stalker_disinfect()` 作为我们的回调来执行更改,从而自己*消毒*它。 +这只是检查程序计数器是否设置为指向 `infect_thunk`,并将程序指针重置回其原始值。 +`infect_thunk` 由 `gum_stalker_infect()` 创建,这是 `gum_stalker_follow()` 用于修改上下文的回调。 +回想一下,虽然一些设置可以代表目标线程进行,但有些必须在目标线程本身的上下文中完成 +(特别是在线程本地存储中设置变量)。好吧,正是 `infect_thunk` 包含该代码。 + +## 杂项 + +希望我们现在已经涵盖了 Stalker 最重要的方面,并对其工作原理提供了良好的背景。 +不过,我们还有一些其他观察结果可能会引起兴趣。 + +### 独占存储 + +AArch64 架构支持[独占加载/存储指令](https://static.docs.arm.com/100934/0100/armv8_a_synchronization_primitives_100934_0100_en.pdf)。 +这些指令旨在用于同步。如果从给定地址执行独占加载,然后尝试对同一位置进行独占存储, +那么 CPU 能够检测在此期间对同一位置的任何其他存储(独占或其他),并且存储失败。 + +显然,这些类型的原语很可能用于互斥锁和信号量等结构。 +多个线程可能会尝试加载信号量的当前计数,测试它是否已满,然后递增并将新值存回以获取信号量。 +这些独占操作非常适合这种情况。但是考虑如果多个线程竞争同一资源会发生什么。 +如果其中一个线程被 Stalker 跟踪,它将总是输掉比赛。 +此外,这些指令很容易被其他类型的 CPU 操作干扰,因此如果我们做一些复杂的事情, +比如在加载和存储之间发出事件,我们将导致它每次都失败,并最终无限循环。 +然而,Stalker 处理这种情况: + +{% highlight c %} +gboolean +gum_stalker_iterator_next (GumStalkerIterator * self, + const cs_insn ** insn) +{ + + ... + + switch (instruction->ci->id) + { + case ARM64_INS_STXR: + case ARM64_INS_STXP: + case ARM64_INS_STXRB: + case ARM64_INS_STXRH: + case ARM64_INS_STLXR: + case ARM64_INS_STLXP: + case ARM64_INS_STLXRB: + case ARM64_INS_STLXRH: + gc->exclusive_load_offset = GUM_INSTRUCTION_OFFSET_NONE; + break; + default: + break; + } + + if (gc->exclusive_load_offset != GUM_INSTRUCTION_OFFSET_NONE) + { + gc->exclusive_load_offset++; + if (gc->exclusive_load_offset == 4) + gc->exclusive_load_offset = GUM_INSTRUCTION_OFFSET_NONE; + } + } + + ... + ... +} + +void +gum_stalker_iterator_keep (GumStalkerIterator * self) +{ + ... + + switch (insn->id) + { + case ARM64_INS_LDAXR: + case ARM64_INS_LDAXP: + case ARM64_INS_LDAXRB: + case ARM64_INS_LDAXRH: + case ARM64_INS_LDXR: + case ARM64_INS_LDXP: + case ARM64_INS_LDXRB: + case ARM64_INS_LDXRH: + gc->exclusive_load_offset = 0; + break; + default: + break; + } + + ... +} +{% endhighlight %} + +在这里,我们可以看到迭代器记录它何时看到独占加载,并跟踪自那以后经过了多少条指令。 +这持续最多四条指令——因为这是根据加载、测试、修改和存储值所需的指令数量通过经验测试确定的。 +然后这用于防止发出任何并非绝对必要的插桩: + +{% highlight c %} + if ((ec->sink_mask & GUM_EXEC) != 0 && + gc->exclusive_load_offset == GUM_INSTRUCTION_OFFSET_NONE) + { + gum_exec_block_write_exec_event_code (block, gc, + GUM_CODE_INTERRUPTIBLE); + } +{% endhighlight %} + +### 耗尽的块 + +虽然我们在开始之前检查以确保 slab 中为我们当前的插桩块留有最小空间(如果低于此最小值则分配一个新的), +但我们无法预测我们在输入块中可能会遇到多长的指令序列。 +确定我们需要多少条输出指令来编写必要的插桩也并不简单 +(我们可能有发出不同类型事件、检查排除范围、虚拟化块末尾找到的指令等的代码)。 +此外,试图允许插桩代码非顺序是充满困难的。 +因此采取的方法是确保每次我们从迭代器读取新指令时,slab 中至少有 1024 字节的空间用于我们的输出。 +如果不是这种情况,那么我们将当前地址存储在 `continuation_real_address` 中并返回 `FALSE`,以便迭代器结束。 + +{% highlight c %} +#define GUM_EXEC_BLOCK_MIN_SIZE 1024 + +static gboolean +gum_exec_block_is_full (GumExecBlock * block) +{ + guint8 * slab_end = block->slab->data + block->slab->size; + + return slab_end - block->code_end < GUM_EXEC_BLOCK_MIN_SIZE; +} + +gboolean +gum_stalker_iterator_next (GumStalkerIterator * self, + const cs_insn ** insn) +{ + ... + + if (gum_exec_block_is_full (block)) + { + gc->continuation_real_address = instruction->end; + return FALSE; + } + + ... +} +{% endhighlight %} + +我们的调用者 `gum_exec_ctx_obtain_block_for()` 遍历迭代器以生成块, +然后就像有一条分支指令到下一条指令一样行动,本质上终止当前块并开始下一个块。 + +{% highlight c %} +static GumExecBlock * +gum_exec_ctx_obtain_block_for (GumExecCtx * ctx, + gpointer real_address, + gpointer * code_address_ptr) +{ + ... + + if (gc.continuation_real_address != NULL) + { + GumBranchTarget continue_target = { 0, }; + + continue_target.absolute_address = gc.continuation_real_address; + continue_target.reg = ARM64_REG_INVALID; + gum_exec_block_write_jmp_transfer_code (block, &continue_target, + GUM_ENTRYGATE (jmp_continuation), &gc); + } + + ... +} +{% endhighlight %} + +这就像在空间不足的指令之前的输入中遇到了以下指令: + +{% highlight asm %} + B label +label: +{% endhighlight %} + +### 系统调用虚拟化 + +系统调用是从用户模式进入内核模式的入口点。这是应用程序要求内核代表其执行操作的方式, +无论是打开文件还是读取网络套接字。在 AArch64 系统上,这是使用 `SVC` 指令执行的, +而在 Intel 上,指令是 `sysenter`。因此,这里的术语 syscall 和 sysenter 是同义使用的。 + +系统调用虚拟化由以下例程执行。我们可以看到我们只在 Linux 系统上做任何事情: + +{% highlight c %} +static GumVirtualizationRequirements +gum_exec_block_virtualize_sysenter_insn (GumExecBlock * block, + GumGeneratorContext * gc) +{ +#ifdef HAVE_LINUX + return gum_exec_block_virtualize_linux_sysenter (block, gc); +#else + return GUM_REQUIRE_RELOCATION; +#endif +} +{% endhighlight %} + +这是因为 `clone` 系统调用。此系统调用创建一个新进程,该进程与父进程共享执行上下文, +例如文件句柄、虚拟地址空间和信号处理程序。本质上,这有效地创建了一个新线程。 +但是当前线程正在被 Stalker 跟踪,而 clone 将创建它的精确副本。 +鉴于 Stalker 上下文是基于每个线程的,我们不应该跟踪这个新的子线程。 + +请注意,对于 AArch64 中的系统调用,前 8 个参数在寄存器 `X0` 到 `X7` 中传递, +系统调用号在 `X8` 中传递,其他参数在堆栈上传递。系统调用的返回值在 `X0` 中返回。 +函数 `gum_exec_block_virtualize_linux_sysenter()` 生成处理此类系统调用所需的插桩代码。 +我们将看下面的伪代码: + +{% highlight c %} +if x8 == __NR_clone: + x0 = do_original_syscall() + if x0 == 0: + goto gc->instruction->begin + return x0 +else: + return do_original_syscall() +{% endhighlight %} + +我们可以看到它首先检查我们是否正在处理 `clone` 系统调用, +否则它只是执行原始系统调用,仅此而已(原始系统调用指令从原始块复制)。 +否则,如果是 clone 系统调用,那么我们再次执行原始系统调用。 +此时,我们有两个执行线程,系统调用确定每个线程将[返回不同的值](http://man7.org/linux/man-pages/man2/clone.2.html)。 +原始线程将接收子线程的 PID 作为其返回值,而子线程将接收值 0。 + +如果我们收到非零值,我们可以像以前一样继续。我们希望继续跟踪线程并允许执行继续下一条指令。 +但是,如果我们收到返回值 0,那么我们就在子线程中。 +因此,我们执行到原始块中下一条指令的分支,确保子线程继续运行而没有任何来自 Stalker 的中断。 + +### 指针认证 + +最后,我们应该注意,较新版本的 iOS 已经[引入](https://ivrodriguez.com/pointer-authentication-on-armv8-3/)了 +[指针认证码](https://events.static.linuxfound.org/sites/events/files/slides/slides_23.pdf)。 +指针认证码 (PAC) 利用指针中未使用的位(虚拟地址的高位通常未使用,因为大多数系统最多有 48 位虚拟地址空间) +来存储认证值。这些值是通过使用原始指针、上下文参数(通常是另一个寄存器的内容)和加密密钥计算出来的。 +这个想法是密钥不能从用户模式读取或写入,并且如果没有访问权限,生成的指针认证码无法被猜测。 + +让我们看下面的代码片段: + +{% highlight asm %} +pacia lr, sp +stp fp, lr, [sp, #-FRAME_SIZE]! +mov fp, sp + +... + +ldp fp, lr, [sp], #FRAME_SIZE +autia lr, sp +ret lr +{% endhighlight %} + +`pacia` 指令结合 `LR`、`SP` 和密钥的值来生成带有认证码 `LR'` 的 `LR` 版本,并存回 `LR` 寄存器。 +此值存储在堆栈中,稍后在函数结束时恢复。`autia` 指令验证 `LR'` 的值。 +这是可能的,因为可以剥离 `LR` 高位中的 PAC 以给出原始 `LR` 值, +并且可以像以前一样使用 `SP` 和密钥重新生成指针认证码。结果与 `LR'` 进行检查。 +如果值不匹配,则指令生成错误。因此,如果存储在堆栈中的 `LR` 值被修改, +或者堆栈指针本身被破坏,那么验证将失败。这对于防止构建需要将返回地址存储在堆栈中的 ROP 链很有用。 +由于 `LR'` 现在存储在堆栈中而不是 `LR`,因此没有密钥就无法伪造有效的返回地址。 + +Frida 在生成代码时也需要考虑到这一点。当从应用程序使用的寄存器读取指针时 +(例如确定间接分支或返回的目的地),有必要在使用之前从地址中剥离这些指针认证码。 +这是使用函数 `gum_arm64_writer_put_xpaci_reg()` 实现的。 diff --git a/_i18n/cn/_docs/swift-api.md b/_i18n/cn/_docs/swift-api.md new file mode 100644 index 00000000..9617cde0 --- /dev/null +++ b/_i18n/cn/_docs/swift-api.md @@ -0,0 +1,7 @@ +## 入门 + +从 https://github.com/frida/frida/releases 下载 Swift 版本。 + +目前请参阅 C 或 JS API 页面以获取类似文档。 + +_请点击上面的“Improve this page”并添加示例。谢谢!_ diff --git a/_i18n/cn/_docs/troubleshooting.md b/_i18n/cn/_docs/troubleshooting.md new file mode 100644 index 00000000..526a888e --- /dev/null +++ b/_i18n/cn/_docs/troubleshooting.md @@ -0,0 +1,36 @@ +如果您在安装或使用 Frida 时遇到问题,这里有一些可能有帮助的提示。如果您遇到的问题未在下面涵盖,请[报告问题]({{ site.organization_url }}/frida-website/issues/new),以便 Frida 社区可以改善每个人的体验。 + +## ValueError: ambiguous name; it matches: + +这意味着您在 `frida.attach()` 中指定的进程名称匹配多个进程。您可以使用 PID 代替: + +{% highlight py %} +session = frida.attach(12345) +{% endhighlight %} + +## SystemError: attach_to_process PTRACE_ATTACH failed: 1 + +这(可能)意味着您没有权限附加到目标进程。该进程可能归另一个用户所有,而您不是 root。您可能忘记启用非子进程的 ptrace。尝试: + +{% highlight bash %} +sudo sysctl kernel.yama.ptrace_scope=0 +{% endhighlight %} + +这也可能是[由于 Magisk Hide](https://github.com/frida/frida/issues/824#issuecomment-479664290)。尝试禁用它并在运行命令之前重新启动。 + +## Failed to spawn: unexpected error while spawning child process 'XXX' (task_for_pid returned '(os/kern) failure') + +在 macOS 上,这可能意味着您没有正确签名 Frida 或缺少权限。例如,如果您通过 SSH 运行 Frida,并且无法响应在*正常*使用下会弹出的身份验证对话框。 + +如果是签名问题,请按照[此过程]({{ site.repository }}#mac-and-ios)操作,否则,尝试: + +{% highlight bash %} +**WARNING: This may weaken security** +sudo security authorizationdb write system.privilege.taskport allow +{% endhighlight %} + +您可能还必须禁用系统完整性保护(SIP)来插桩系统二进制文件,但是,再次强调,**/!\ 这将削弱安全性 /!\**。 + +## ImportError: dynamic module does not define init function (init_frida) + +当尝试在 python 3.x 中使用为 python 2.x 编译的 `frida-python` 时,或者反之亦然,会出现此错误或其他类似错误消息。检查您正在运行哪个 python 解释器以及使用了哪个 `PYTHONPATH` / `sys.path`。 diff --git a/_i18n/cn/_posts/2014-01-05-frida-1-0-5-released.markdown b/_i18n/cn/_posts/2014-01-05-frida-1-0-5-released.markdown new file mode 100644 index 00000000..77af87cd --- /dev/null +++ b/_i18n/cn/_posts/2014-01-05-frida-1-0-5-released.markdown @@ -0,0 +1,13 @@ +--- +layout: news_item +title: 'Frida 1.0.5 发布' +date: 2014-01-05 23:00:00 +0100 +author: oleavr +version: 1.0.5 +categories: [release] +--- + +此版本改进了 [frida-trace][],支持从文件系统上的脚本自动生成、加载和重新加载函数处理程序。查看我们的 [Quick-start guide](/docs/quickstart) 进行演练。此版本中的另一个新功能是 Py3k 支持,可从所有平台上的 [PyPI][] 获得。 + +[frida-trace]: https://github.com/frida/frida-python/blob/master/src/frida/tracer.py +[PyPI]: https://pypi.python.org/pypi/frida diff --git a/_i18n/cn/_posts/2014-01-11-frida-1-0-6-released.markdown b/_i18n/cn/_posts/2014-01-11-frida-1-0-6-released.markdown new file mode 100644 index 00000000..b01b78d7 --- /dev/null +++ b/_i18n/cn/_posts/2014-01-11-frida-1-0-6-released.markdown @@ -0,0 +1,15 @@ +--- +layout: news_item +title: 'Frida 1.0.6 发布' +date: 2014-01-11 23:00:00 +0100 +author: oleavr +version: 1.0.6 +categories: [release] +--- + +此版本简化了许可并修复了自 HN 发布以来社区报告的错误。 + +主要是: +- 将剩余的 GPLv3+ Frida 组件重新许可为 LGPLv2.1+(与 frida-gum 相同)。 +- Tracer 在 64 位上工作,函数地址在上层范围内 +- Linux 构建链接 Frida 自己的库,而不是构建机器的相应库。 diff --git a/_i18n/cn/_posts/2014-01-12-frida-1-0-7-released.markdown b/_i18n/cn/_posts/2014-01-12-frida-1-0-7-released.markdown new file mode 100644 index 00000000..e846c0a5 --- /dev/null +++ b/_i18n/cn/_posts/2014-01-12-frida-1-0-7-released.markdown @@ -0,0 +1,27 @@ +--- +layout: news_item +title: 'Frida 1.0.7 发布' +date: 2014-01-12 23:00:00 +0100 +author: oleavr +version: 1.0.7 +categories: [release] +--- + +此版本在命令行工具中带来了 USB 设备支持,并添加了 `frida-ps` 用于枚举本地和远程进程。 + +例如,要枚举系留 iOS 设备上的进程: +{% highlight bash %} +$ frida-ps -U +{% endhighlight %} + +`frida-trace` 和 `frida-discover` 也接受 `-U` 开关。 + +关于如何在您的 iOS 设备上设置此功能的文档将很快添加到网站上。 + +然而,这并不是最令人兴奋的部分。从这个版本开始,Frida 获得了自 HN 发布以来的第一个贡献。[Pete Morici](https://github.com/pmorici) 潜入并贡献了对在 `frida-trace` 中指定模块相对函数的支持: + +{% highlight bash %} +$ frida-trace -a 'kernel32.dll+0x1234' +{% endhighlight %} + +享受吧! diff --git a/_i18n/cn/_posts/2014-01-15-frida-1-0-8-released.markdown b/_i18n/cn/_posts/2014-01-15-frida-1-0-8-released.markdown new file mode 100644 index 00000000..e6b9ae6d --- /dev/null +++ b/_i18n/cn/_posts/2014-01-15-frida-1-0-8-released.markdown @@ -0,0 +1,13 @@ +--- +layout: news_item +title: 'Frida 1.0.8 发布' +date: 2014-01-15 23:00:00 +0100 +author: oleavr +version: 1.0.8 +categories: [release] +--- + +我们刚刚推出了一个错误修复版本: +- 支持注入 Mac App Store 应用程序 +- 消除 iOS 守护进程自动启动问题 +- 注入后不久不再有 iOS 崩溃 diff --git a/_i18n/cn/_posts/2014-01-25-frida-1-0-9-released.markdown b/_i18n/cn/_posts/2014-01-25-frida-1-0-9-released.markdown new file mode 100644 index 00000000..9438ccdd --- /dev/null +++ b/_i18n/cn/_posts/2014-01-25-frida-1-0-9-released.markdown @@ -0,0 +1,30 @@ +--- +layout: news_item +title: 'Frida 1.0.9 发布' +date: 2014-01-25 23:00:00 +0100 +author: oleavr +version: 1.0.9 +categories: [release] +--- + +另一个版本 —— 这次有一些新功能: + +- Mac 和 iOS 的 Objective-C 集成。这是一个例子来吊起你的胃口: + +{% highlight js %} +const UIAlertView = ObjC.use('UIAlertView'); /* iOS */ +ObjC.schedule(ObjC.mainQueue, () => { + const view = UIAlertView.alloc().initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles_( + "Frida", + "Hello from Frida", + ptr("0"), + "OK", + ptr("0")); + view.show(); + view.release(); +}); +{% endhighlight %} + +- `Module.enumerateExports()` 现在也枚举导出的变量,而不仅仅是函数。`onMatch` 回调接收一个 `exp` 对象,其中 `type` 字段是 `function` 或 `variable`。 + +要获得有关 ObjC 集成的完整信息,请查看 [JavaScript API reference](/docs/javascript-api/)。 diff --git a/_i18n/cn/_posts/2014-02-16-frida-1-0-10-released.markdown b/_i18n/cn/_posts/2014-02-16-frida-1-0-10-released.markdown new file mode 100644 index 00000000..729bcdfd --- /dev/null +++ b/_i18n/cn/_posts/2014-02-16-frida-1-0-10-released.markdown @@ -0,0 +1,46 @@ +--- +layout: news_item +title: 'Frida 1.0.10 发布' +date: 2014-02-16 01:51:00 +0100 +author: oleavr +version: 1.0.10 +categories: [release] +--- + +此版本带来了一些改进: + +- `Interceptor` 现在与 iOS/ARM 上更多的函数兼容。 +- 一个名为 `frida-repl` 的新 CLI 工具为您提供了一个基本的 REPL,以便从目标进程内部试验 JavaScript API。 +- 传递给 `Interceptor.attach()` 的 `onLeave` 回调现在可以通过调用 `retval.replace()` 来替换返回值。 +- 传递给 `Interceptor.attach()` 的 `onEnter` 和 `onLeave` 回调都可以访问 `this.errno` (UNIX) 或 `this.lastError` (Windows) 以检查或操作当前线程的最后一个系统错误。 + +以下是如何结合后三个来模拟 Mac 上运行的特定进程的网络条件: + +{% highlight bash %} +~ $ frida-repl TargetApp +{% endhighlight %} + +然后粘贴: + +{% highlight js %} +callbacks = { \ + onEnter(args) { \ + args[0] = ptr(-1); // Avoid side-effects on socket \ + }, \ + onLeave(retval) { \ + const ECONNREFUSED = 61; \ + this.errno = ECONNREFUSED; \ + retval.replace(-1); \ + } \ +}; \ +Module.enumerateExports("libsystem_kernel.dylib", { \ + onMatch(exp) { \ + if (exp.name.indexOf("connect") === 0 && exp.name.indexOf("connectx") !== 0) { \ + Interceptor.attach(exp.address, callbacks); \ + } \ + }, \ + onComplete() {} \ +}); +{% endhighlight %} + +享受吧! diff --git a/_i18n/cn/_posts/2014-03-09-frida-1-0-11-released.markdown b/_i18n/cn/_posts/2014-03-09-frida-1-0-11-released.markdown new file mode 100644 index 00000000..f659cdcb --- /dev/null +++ b/_i18n/cn/_posts/2014-03-09-frida-1-0-11-released.markdown @@ -0,0 +1,17 @@ +--- +layout: news_item +title: 'Frida 1.0.11 发布' +date: 2014-03-09 19:00:00 +0100 +author: oleavr +version: 1.0.11 +categories: [release] +--- + +你们中的一些人在注入 Windows 上的进程时遇到了问题,以及在 iOS 上崩溃。这是一个新版本,为 Frida 的内部带来了一些重大改进: + +- V8 已升级到 3.25 以修复 iOS 稳定性问题。这也意味着新功能(如 ECMAScript 6)和所有平台上的性能改进。另一个不错的方面是 Frida 现在依赖于在 64 位 ARM 上运行的 V8 版本,这为将 Frida 本身移植到 AArch64 铺平了道路。 +- Windows 注入器学会了一些新技巧,可以让您进入更多进程。还在 Windows 构建系统中发现了一个配置错误,这解释了为什么你们中的一些人无法注入某些进程。 +- 对于那些在 Windows 上构建 Frida 的人来说,那里的构建系统现在依赖于 VS2013。这意味着不再支持 XP,尽管如果你们中仍有人依赖它,仍然可以使用 `v120_xp` 工具链进行构建,所以如果这对您来说是一个破坏交易的问题,请告诉我。 +- 最近添加的对 `this.lastError` (Windows) 的支持现在可以正常工作了。 + +目前就这些。让我们知道您的想法,如果您喜欢 Frida,请帮助宣传!:) diff --git a/_i18n/cn/_posts/2014-04-17-frida-1-2-0-released.markdown b/_i18n/cn/_posts/2014-04-17-frida-1-2-0-released.markdown new file mode 100644 index 00000000..8756468a --- /dev/null +++ b/_i18n/cn/_posts/2014-04-17-frida-1-2-0-released.markdown @@ -0,0 +1,14 @@ +--- +layout: news_item +title: 'Frida 1.2.0 发布' +date: 2014-04-17 17:00:00 +0100 +author: oleavr +version: 1.2.0 +categories: [release] +--- + +现在是发布时间,Frida 1.2.0 终于发布了!除了错误修复之外,此版本还引入了全新的 ARM64 支持,这对于那些在 iPhone 5S 或 iPad Air 上使用 Frida 的人来说非常有用。您现在可以注入 64 位和 32 位进程,就像在 Mac 和 Windows 上一样。 + +此版本还提高了 ARM32 上的稳定性,以前附加到短函数会导致未定义的行为。 + +享受吧! diff --git a/_i18n/cn/_posts/2014-04-21-frida-1-2-1-released.markdown b/_i18n/cn/_posts/2014-04-21-frida-1-2-1-released.markdown new file mode 100644 index 00000000..84f10a36 --- /dev/null +++ b/_i18n/cn/_posts/2014-04-21-frida-1-2-1-released.markdown @@ -0,0 +1,14 @@ +--- +layout: news_item +title: 'Frida 1.2.1 发布' +date: 2014-04-21 16:00:00 +0100 +author: oleavr +version: 1.2.1 +categories: [release] +--- + +在跟踪 Apple 的加密 API 时获得了一些乐趣,这导致发现了一些错误。所以这里是 1.2.1,带来了一些关键的 ARM 相关错误修复: + +- ARM32: 修复了由于 Apple 的 ABI 与 AAPCS 相比关于 `r9` 的 ABI 差异而在 ARM32 上的 V8 中寄存器破坏问题引起的崩溃。 +- ARM32: 修复 ARM32/Thumb 重定位器分支重写,用于立即同模式分支。 +- ARM64: 改进 ARM64 重定位器以支持重写 `b` 和 `bl`。 diff --git a/_i18n/cn/_posts/2014-05-04-frida-1-4-0-released.markdown b/_i18n/cn/_posts/2014-05-04-frida-1-4-0-released.markdown new file mode 100644 index 00000000..dd47ab09 --- /dev/null +++ b/_i18n/cn/_posts/2014-05-04-frida-1-4-0-released.markdown @@ -0,0 +1,12 @@ +--- +layout: news_item +title: 'Frida 1.4.0 发布' +date: 2014-05-04 03:00:00 +0100 +author: oleavr +version: 1.4.0 +categories: [release] +--- + +有人说 Android 吗?Frida 1.4.0 发布了,具有全新的 Android 支持!查看 [here](/docs/android/) 的文档以开始使用。此版本中的另一个新功能是 Frida 现在由惊人的 [Capstone Disassembly Engine](http://www.capstone-engine.org/) 驱动,这意味着我们的跨平台代码检测更加强大。它还为在未来版本中将仅限 x86 的 [stealth tracing](https://github.com/frida/frida-gum/blob/41dbb1d27a784fd8b5e233929a317cd41beb9c2d/tests/core/arch-x86/stalker-x86.c#L78) 带到新架构铺平了道路。 + +享受吧! diff --git a/_i18n/cn/_posts/2014-05-14-frida-1-4-1-released.markdown b/_i18n/cn/_posts/2014-05-14-frida-1-4-1-released.markdown new file mode 100644 index 00000000..1e3b6835 --- /dev/null +++ b/_i18n/cn/_posts/2014-05-14-frida-1-4-1-released.markdown @@ -0,0 +1,12 @@ +--- +layout: news_item +title: 'Frida 1.4.1 发布' +date: 2014-05-14 01:00:00 +0100 +author: oleavr +version: 1.4.1 +categories: [release] +--- + +有兴趣在 Windows 或 Linux 上生成进程,而不仅仅是在 Mac 上吗?或者也许您被 Linux 注入器崩溃您的进程而不是让您注入它们所困扰?或者也许您的函数名太长,以至于 frida-trace 溢出了 Windows 上的最大文件名长度?好吧,如果是以上任何一种情况,或者都不是,那么 Frida 1.4.1 适合您! + +感谢 Guillaume 和 Pedro 让这个版本变得很棒。请继续提交拉取请求和错误报告! diff --git a/_i18n/cn/_posts/2014-05-14-frida-1-4-2-released.markdown b/_i18n/cn/_posts/2014-05-14-frida-1-4-2-released.markdown new file mode 100644 index 00000000..0e9dbf6d --- /dev/null +++ b/_i18n/cn/_posts/2014-05-14-frida-1-4-2-released.markdown @@ -0,0 +1,12 @@ +--- +layout: news_item +title: 'Frida 1.4.2 发布' +date: 2014-05-14 21:00:00 +0100 +author: oleavr +version: 1.4.2 +categories: [release] +--- + +只是一个快速的错误修复版本,以消除 [this annoying bug](https://github.com/frida/frida-core/issues/5),这在所有支持的 x86 操作系统上都是可重现的。感谢 Guillaume 追踪到了这一个。 + +作为奖励,frida-repl 现在也可以在 Windows 上运行。REPL 快乐! diff --git a/_i18n/cn/_posts/2014-05-29-frida-1-6-0-released.markdown b/_i18n/cn/_posts/2014-05-29-frida-1-6-0-released.markdown new file mode 100644 index 00000000..e93d58c4 --- /dev/null +++ b/_i18n/cn/_posts/2014-05-29-frida-1-6-0-released.markdown @@ -0,0 +1,48 @@ +--- +layout: news_item +title: 'Frida 1.6.0 发布' +date: 2014-05-29 23:00:00 +0100 +author: oleavr +version: 1.6.0 +categories: [release] +--- + +正如你们中的一些人可能已经注意到的那样,Frida 最近获得了全新的 Android 支持,允许您像在 Windows、Mac、Linux 和 iOS 上一样轻松地检测代码。这听起来可能很酷,但 Android 确实运行大量 Java 代码,这意味着您只能观察该代码正在做的任何事情的本机副作用。您当然可以使用 Frida 的 FFI API 闯入 VM,但是嘿,Frida 不应该只为您做那个肮脏的管道吗?当然应该! + +这是它在行动中的样子: + +{% highlight js %} +Dalvik.perform(() => { + const Activity = Dalvik.use('android.app.Activity'); + Activity.onResume.implementation = function () { + send('onResume() got called! Let's call the original implementation'); + this.onResume(); + }; +}); +{% endhighlight %} + +`Dalvik.perform()` 调用负责将您的线程附加到 VM,并且在来自 Java 的回调中不是必需的。此外,第一次使用给定的类名调用 `Dalvik.use()` 时,Frida 将询问 VM 并即时构建 JavaScript 包装器。上面我们请求 [Activity](https://developer.android.com/reference/android/app/Activity.html) 类并用我们自己的版本替换其 `onResume` 的实现,并在向调试器(在您的 Windows、Mac 或 Linux 机器上运行)发送消息后继续调用原始实现。您当然可以选择根本不调用原始实现,并模拟其行为。或者,也许您想模拟错误场景: + +{% highlight js %} +Dalvik.perform(() => { + const Activity = Dalvik.use('android.app.Activity'); + const Exception = Dalvik.use('java.lang.Exception'); + Activity.onResume.implementation = function () { + throw Exception.$new('Oh noes!'); + }; +}); +{% endhighlight %} + +所以您刚刚实例化了一个 Java 异常,并直接从您的 `Activity.onResume` 的 JavaScript 实现中抛出它。 + +此版本还附带了一些其他运行时好东西: + +- `Memory.copy(dst, src, n)`: 就像 memcpy +- `Memory.dup(mem, size)`: `Memory.alloc()` 后跟 `Memory.copy()` 的简写 +- `Memory.writeXXX()`: 缺少的 `Memory.read()` 对应物:S8, S16, U16, S32, U32, S64, U64, ByteArray, Utf16String 和 AnsiString +- `Process.pointerSize` 使您的脚本更便携 +- `NativePointer` 实例现在有一个方便的 `isNull()` 方法 +- `NULL` 常量,所以您不必到处做 `ptr("0")` +- `WeakRef.bind(value, fn)` 和 `WeakRef.unbind(id)` 给铁杆玩家:前者监视 `value`,以便一旦 `value` 被垃圾收集,或者脚本即将被卸载,`fn` 就会被调用。它返回一个 id,您可以将其传递给 `unbind()` 以进行显式清理。如果您正在构建语言绑定,其中需要在不再需要 JS 值时释放本机资源,则此 API 很有用。 + +享受吧! diff --git a/_i18n/cn/_posts/2014-07-26-frida-1-6-1-released.markdown b/_i18n/cn/_posts/2014-07-26-frida-1-6-1-released.markdown new file mode 100644 index 00000000..128573a1 --- /dev/null +++ b/_i18n/cn/_posts/2014-07-26-frida-1-6-1-released.markdown @@ -0,0 +1,23 @@ +--- +layout: news_item +title: 'Frida 1.6.1 发布' +date: 2014-07-26 19:00:00 +0100 +author: oleavr +version: 1.6.1 +categories: [release] +--- + +是时候发布错误修复版本了。亮点: + +- 与 ARM64 上的 Pangu iOS 越狱兼容。问题在于 RWX 页面不像以前使用 evad3rs 越狱那样可用。 +- 修复分离时偶尔的目标进程崩溃。 +- 修复在第一次建立失败后第二次尝试附加到进程时的崩溃。这主要影响 Android 用户,但在使用 `frida-server` 时可能会发生在任何操作系统上。 +- 在 Linux/x86-64 和 Android/ARM 上更快更可靠的注入。 +- 修复阻止在 Windows 上 hook HeapFree 和朋友的问题。 +- 升级 GLib、libgee、json-glib 和 Vala 依赖项以提高性能和错误修复。 +- 不再有资源泄漏。如果您发现任何问题,请报告。 + +此外,自 1.6.0 以来,正如我的 [blog post][] 中所涵盖的那样,现在有一个功能齐全的 [binding for Qml][]。这对于那些构建图形跨平台工具的人来说应该很有趣。 + +[blog post]: https://medium.com/@oleavr/build-a-debugger-in-5-minutes-1-5-51dce98c3544 +[binding for Qml]: https://github.com/frida/frida-qml diff --git a/_i18n/cn/_posts/2014-08-03-frida-1-6-2-released.markdown b/_i18n/cn/_posts/2014-08-03-frida-1-6-2-released.markdown new file mode 100644 index 00000000..3adcfb4c --- /dev/null +++ b/_i18n/cn/_posts/2014-08-03-frida-1-6-2-released.markdown @@ -0,0 +1,27 @@ +--- +layout: news_item +title: 'Frida 1.6.2 发布' +date: 2014-08-03 17:00:00 +0100 +author: oleavr +version: 1.6.2 +categories: [release] +--- + +现在是发布时间,这次我们为您带来的不仅仅是错误修复。认识一下 `Instruction.parse()`: + +{% highlight js %} +const a = Instruction.parse(ptr('0x1234')); +const b = Instruction.parse(a.next); +console.log(a); +console.log(b); +{% endhighlight %} + +输出: +{% highlight nasm %} +push rbp +mov rbp, rsp +{% endhighlight %} + +您问这是如何实现的?那是很酷的部分。Frida 已经在幕后使用了惊人的 [Capstone disassembly framework](https://www.capstone-engine.org/),因此将其提供给 JavaScript 运行时非常有意义。查看 [JavaScript API Reference](/docs/javascript-api/) 了解所有详细信息。 + +享受吧! diff --git a/_i18n/cn/_posts/2014-08-24-frida-1-6-3-released.markdown b/_i18n/cn/_posts/2014-08-24-frida-1-6-3-released.markdown new file mode 100644 index 00000000..3aba8a13 --- /dev/null +++ b/_i18n/cn/_posts/2014-08-24-frida-1-6-3-released.markdown @@ -0,0 +1,30 @@ +--- +layout: news_item +title: 'Frida 1.6.3 发布' +date: 2014-08-24 23:00:00 +0100 +author: oleavr +version: 1.6.3 +categories: [release] +--- + +这个最新版本包括一系列增强功能和错误修复。一些亮点: + +- Frida 内部的其余部分已从 udis86 迁移到 Capstone,这意味着我们的 Stalker 现在能够跟踪具有最新 x86 指令的二进制文件。这项工作的一部分还包括在 Windows 和 Mac 上的 32 位和 64 位二进制文件上对其进行实战测试,所有已知问题现已解决。 + +- `Memory.protect()` 已添加到 JavaScript API,允许您轻松更改页面保护。例如: + +{% highlight js %} +Memory.protect(ptr("0x1234"), 4096, 'rw-'); +{% endhighlight %} + +- `Process.enumerateThreads()` 省略了 Frida 自己的线程,所以您不必担心它们。 + +- Python 3 二进制文件现在针对 Python 3.4 构建。 + +随着这个版本的发布,让我们谈谈 [CryptoShark](https://github.com/frida/cryptoshark): + + + +获取预构建的 Windows 二进制文件 [here](https://build.frida.re/frida/windows/Win32-Release/bin/cryptoshark-0.1.1.exe),或者如果您想在 Mac 或 Linux 上试用它,请从源代码构建它。 + +享受吧! diff --git a/_i18n/cn/_posts/2014-10-19-frida-1-6-4-released.markdown b/_i18n/cn/_posts/2014-10-19-frida-1-6-4-released.markdown new file mode 100644 index 00000000..775fb861 --- /dev/null +++ b/_i18n/cn/_posts/2014-10-19-frida-1-6-4-released.markdown @@ -0,0 +1,27 @@ +--- +layout: news_item +title: 'Frida 1.6.4 发布' +date: 2014-10-19 04:04:00 +0100 +author: oleavr +version: 1.6.4 +categories: [release] +--- + +是时候发布错误修复版本了! + +Stalker 改进: + +- 引擎不再为每个被跟踪的线程预分配 256 MB 的固定块,现在以重入安全的方式动态增长。 +- 消除了缓存查找逻辑中的一个错误,即某些块总是会导致缓存未命中。因此,这些块每次即将执行时都会重新编译,从而减慢执行速度并用越来越多的条目堵塞缓存,最终耗尽内存。 +- 现在可以正确处理 RIP 相对 `cmpxchg` 指令的重定位。 + +更好的 Dalvik 集成 (Android): + +- 现在可以加载应用程序自己的类。 +- 修复了几个编组错误。 + +脚本运行时: + +- 具有相同目标地址的多个 NativeFunction 不再导致 use-after-free。 + +此外,[CryptoShark 0.1.2](https://github.com/frida/cryptoshark) 已经发布,具有升级的 Frida 引擎和大量性能改进,因此 GUI 能够跟上 Stalker。趁热去拿吧! diff --git a/_i18n/cn/_posts/2014-10-29-frida-1-6-5-released.markdown b/_i18n/cn/_posts/2014-10-29-frida-1-6-5-released.markdown new file mode 100644 index 00000000..539c8fbe --- /dev/null +++ b/_i18n/cn/_posts/2014-10-29-frida-1-6-5-released.markdown @@ -0,0 +1,15 @@ +--- +layout: news_item +title: 'Frida 1.6.5 发布' +date: 2014-10-29 23:00:00 +0100 +author: oleavr +version: 1.6.5 +categories: [release] +--- + +现在是发布时间,也是修复一些错误的时间: + +- 现在支持 iOS 8.1,ARM64 支持比以往任何时候都好。 +- iOS USB 传输在向设备发送突发数据时不再断开连接。这通常会在使用 `frida-trace` 并跟踪一堆函数时发生,导致通过线路发送突发数据。这实际上是 [影响 Mac 和 iOS 的通用网络问题](https://bugzilla.gnome.org/show_bug.cgi?id=11059),但在使用 Frida 与系留 iOS 设备一起使用时非常容易重现。 +- 消除了 Python 解释器关闭时的崩溃。 +- `frida-trace` 脚本中的 `onEnter` 和 `onLeave` 回调现在在调用时将 `this` 绑定到正确的对象,这意味着它绑定到特定于该线程和调用的对象,而不是由所有线程和调用共享的对象。 diff --git a/_i18n/cn/_posts/2014-11-03-frida-1-6-6-released.markdown b/_i18n/cn/_posts/2014-11-03-frida-1-6-6-released.markdown new file mode 100644 index 00000000..96711835 --- /dev/null +++ b/_i18n/cn/_posts/2014-11-03-frida-1-6-6-released.markdown @@ -0,0 +1,10 @@ +--- +layout: news_item +title: 'Frida 1.6.6 发布' +date: 2014-11-03 14:00:00 +0100 +author: oleavr +version: 1.6.6 +categories: [release] +--- + +**此版本引入了严重的回归,并很快被撤下并替换为 [1.6.7]({% post_url _i18n/cn/2014-11-03-frida-1-6-7-released %})。** diff --git a/_i18n/cn/_posts/2014-11-03-frida-1-6-7-released.markdown b/_i18n/cn/_posts/2014-11-03-frida-1-6-7-released.markdown new file mode 100644 index 00000000..dce4e9b7 --- /dev/null +++ b/_i18n/cn/_posts/2014-11-03-frida-1-6-7-released.markdown @@ -0,0 +1,16 @@ +--- +layout: news_item +title: 'Frida 1.6.7 发布' +date: 2014-11-03 16:00:00 +0100 +author: oleavr +version: 1.6.7 +categories: [release] +--- + +厌倦了等待 Frida 附加到 64 位 Mac 或 iOS 系统上的 32 位进程?或者也许 `frida-trace` 需要一段时间来解析函数?如果是以上任何一种情况,或者都不是,那么这个版本适合您! + +附加到 Mac/iOS 主机上的 32 位进程已得到优化,现在只需几毫秒,而不是几秒钟。但这仅限于 Darwin 操作系统;此版本还加快了所有操作系统上模块导出的枚举速度。现在快了 75%,在使用 `frida-trace` 并等待它解析函数时应该非常明显。 + +此外,作为额外的奖励,附加到多个进程时的拆卸不再在 Darwin 和 Linux 上崩溃。 + +享受吧! diff --git a/_i18n/cn/_posts/2014-11-18-frida-1-6-8-released.markdown b/_i18n/cn/_posts/2014-11-18-frida-1-6-8-released.markdown new file mode 100644 index 00000000..a312ce81 --- /dev/null +++ b/_i18n/cn/_posts/2014-11-18-frida-1-6-8-released.markdown @@ -0,0 +1,10 @@ +--- +layout: news_item +title: 'Frida 1.6.8 发布' +date: 2014-11-18 02:00:00 +0100 +author: oleavr +version: 1.6.8 +categories: [release] +--- + +只是一个小的错误修复版本,修复了 Mac 上的 `spawn()`,并解决了一些拆卸问题。享受吧! diff --git a/_i18n/cn/_posts/2015-02-23-frida-1-8-0-released.markdown b/_i18n/cn/_posts/2015-02-23-frida-1-8-0-released.markdown new file mode 100644 index 00000000..41b33dfa --- /dev/null +++ b/_i18n/cn/_posts/2015-02-23-frida-1-8-0-released.markdown @@ -0,0 +1,10 @@ +--- +layout: news_item +title: 'Frida 1.8.0 发布' +date: 2015-02-23 01:33:00 +0100 +author: oleavr +version: 1.8.0 +categories: [release] +--- + +**此版本在 iOS 上引入了严重的回归,并很快从我们的 Cydia 存储库中撤下,尽管它在等待被 [2.0.0]({% post_url _i18n/cn/2015-03-01-frida-2-0-0-released %}) 替换期间可用于 Mac、Linux 和 Android。** diff --git a/_i18n/cn/_posts/2015-03-01-frida-2-0-0-released.markdown b/_i18n/cn/_posts/2015-03-01-frida-2-0-0-released.markdown new file mode 100644 index 00000000..099e802e --- /dev/null +++ b/_i18n/cn/_posts/2015-03-01-frida-2-0-0-released.markdown @@ -0,0 +1,39 @@ +--- +layout: news_item +title: 'Frida 2.0.0 发布' +date: 2015-03-01 01:00:00 +0100 +author: oleavr +version: 2.0.0 +categories: [release] +--- + +是时候发布一个令人兴奋的新版本了!主要变化包括: + +- Mac 和 iOS 上不再有内核恐慌!阅读完整故事 [here](https://medium.com/@oleavr/diy-kernel-panic-os-x-and-ios-in-10-loc-c250d9649159)。 +- Mac 和 iOS 注入器执行 Frida dylib 的手动映射。这意味着我们能够附加到受到严格沙盒保护的进程。 +- 像 *frida-trace*、*frida-repl* 等 CLI 工具对生成进程有了全新的支持: +{% highlight bash %} +$ frida-trace -i 'open*' -i 'read*' /bin/cat /etc/resolv.conf + 27 ms open$NOCANCEL() + 28 ms read$NOCANCEL() + 28 ms read$NOCANCEL() + 28 ms read$NOCANCEL() +Target process terminated. +Stopping... +$ +{% endhighlight %} +- *frida-repl* 和 *frida-discover* 中的可用性改进。 +- 第一次调用 `DeviceManager.enumerate_devices()` 做得更好,并且还为您提供当前连接的 iOS 设备,因此对于简单的应用程序或脚本,如果您要求设备已经存在,您不再需要订阅更新。 +- python API 现在为您提供 `frida.get_usb_device(timeout = 0)` 和 `frida.get_remote_device()` 以便轻松访问 iOS 和远程/Android 设备。 +- 传递给 `Interceptor.attach()` 的 `onEnter` 和 `onLeave` 回调可以访问 `this.registers` 以检查 CPU 寄存器,这在处理自定义调用约定时非常有用。 +- `console.log()` 记录到应用程序侧的控制台而不是目标进程。这个变化实际上是我们不得不为此版本提升主要版本的原因。 +- Android 5.0 兼容性,模 ART 支持。 +- 对 Android/x86 的全新支持。除了 Dalvik 集成外,一切正常;如果您想通过拉取请求帮助解决此问题,请联系我们! + +想帮忙吗?看看我们的 [GSoC 2015 Ideas Page](/docs/gsoc-ideas-2015/) 以了解我们下一步想去哪里。 + +享受吧! + +**凌晨 2 点更新:** 一个 iOS 问题在最终测试中遗漏了,所以我们刚刚推送了 2.0.1 来解决这个问题。 + +**晚上 11 点更新:** 感谢您的出色反馈,我们在具有某些 iOS 设备配置的 Windows 上使用 Frida 时发现了一个严重错误。请升级到 2.0.2,如果您遇到任何问题,请告诉我们。 diff --git a/_i18n/cn/_posts/2015-03-01-frida-2-0-1-released.markdown b/_i18n/cn/_posts/2015-03-01-frida-2-0-1-released.markdown new file mode 100644 index 00000000..3729bf07 --- /dev/null +++ b/_i18n/cn/_posts/2015-03-01-frida-2-0-1-released.markdown @@ -0,0 +1,10 @@ +--- +layout: news_item +title: 'Frida 2.0.1 发布' +date: 2015-03-01 02:00:00 +0100 +author: oleavr +version: 2.0.1 +categories: [release] +--- + +只是一个快速的错误修复版本,以修复在 2.0.0 的最终测试中遗漏的 iOS 问题。享受吧! diff --git a/_i18n/cn/_posts/2015-03-01-frida-2-0-2-released.markdown b/_i18n/cn/_posts/2015-03-01-frida-2-0-2-released.markdown new file mode 100644 index 00000000..c2561879 --- /dev/null +++ b/_i18n/cn/_posts/2015-03-01-frida-2-0-2-released.markdown @@ -0,0 +1,12 @@ +--- +layout: news_item +title: 'Frida 2.0.2 发布' +date: 2015-03-01 11:00:00 +0100 +author: oleavr +version: 2.0.2 +categories: [release] +--- + +感谢您的出色反馈,我们刚刚消除了在具有某些 iOS 设备配置的 Windows 上使用 Frida 时的崩溃。由于这是一个非常重要的用例,我们决定发布一个没有任何其他更改的热修复版本。 + +请继续提交错误报告! diff --git a/_i18n/cn/_posts/2015-03-20-frida-3-0-0-released.markdown b/_i18n/cn/_posts/2015-03-20-frida-3-0-0-released.markdown new file mode 100644 index 00000000..9d0fc701 --- /dev/null +++ b/_i18n/cn/_posts/2015-03-20-frida-3-0-0-released.markdown @@ -0,0 +1,42 @@ +--- +layout: news_item +title: 'Frida 3.0.0 发布' +date: 2015-03-20 23:00:00 +0100 +author: oleavr +version: 3.0.0 +categories: [release] +--- + +您可能想知道: + +> 为什么是 Python API,却是 JavaScript 调试逻辑? + +好吧,您现在可以这样做: + +{% highlight sh %} +$ npm install frida +{% endhighlight %} + +我们刚刚为您带来了全新的 [Node.js 绑定](https://github.com/frida/frida-node),它们是完全异步的: + +{% gist 6ecae99945ccba47427a %} + +查看 [示例](https://github.com/frida/frida-node/blob/46a5f92203ab86978a2af68d6c926d6d2b63fbe7/examples/interactive.js) 以了解 API 是什么样子的。它几乎是 Python 绑定提供的 API 的 1:1 映射,但遵循 Node.js / JavaScript 约定,如 camelCased 方法名、返回 ES6 *Promise* 对象而不是阻塞的方法等。 + +现在,将其与 [NW.js](https://github.com/nwjs/nw.js/) 结合使用,您可以完全使用 HTML、CSS 和 JavaScript 构建自己的桌面应用程序。 + +所以,全新的 Node.js 绑定;太棒了!然而,我们并没有止步于此。但首先,关于未来的一两句话。我很高兴地宣布,我刚刚创办了一家公司,旨在赞助 Frida 的兼职开发。通过提供逆向工程和软件开发专业知识,目标是产生足够的收入来支付我的账单,并留出一些时间来开发 Frida。从长远来看,我希望也会有帮助添加功能或将 Frida 集成到第三方产品的需求。与此同时,如果您认识正在寻找逆向工程或软件开发专业知识的人,如果您能好心地推荐他们与我联系,我将不胜感激。详情请见 [我的简历](https://github.com/oleavr/cv/raw/master/oleavr.pdf)。 + +撇开这些不谈,让我们回到发布。接下来:32 位 Linux 支持!甚至 *Stalker* 也已被移植。不仅如此,Linux 后端甚至可以像我们在其他平台上一样进行跨架构注入。这意味着 64 位 Frida 进程,例如您的 Python 解释器,可以注入到 32 位进程中。反之亦然。 + +另一个很棒的更新是 [Tyilo](https://github.com/Tyilo) 为 *frida-trace* 贡献了 [改进](https://github.com/frida/frida-python/commit/daf1a310670588e5672af2205658598be342c2e2),因此它现在使用手册页自动生成日志处理程序。太棒了,是吧?但还有更多好东西: + +- *frida-server* 端口现在被回收,所以如果您在 Android 上使用 Frida,您不必一直转发端口,除非您实际上同时附加到多个进程。 +- Linux 和 Android `spawn()` 支持已得到改进,也支持 PIE 二进制文件。 +- Android 稳定性和兼容性改进。 +- Mac 和 Linux 构建系统已经过改进,可以轻松构建您关心的部分;甚至可能还有一些您甚至不知道存在的组件,以前默认情况下未构建。 +- Python 绑定有一个小的简化,所以不再是 `frida.attach(pid).session.create_script()`,而仅仅是 `frida.attach(pid).create_script()`。这就像全新的 Node.js 绑定一样,也是我们不得不提升主要版本的原因。 + +这就是它的要点。请通过在网络上传播这篇文章来帮助宣传。作为一个开源项目,我们还很小,所以口碑营销对我们来说意义重大。 + +享受吧! diff --git a/_i18n/cn/_posts/2015-05-08-frida-4-0-0-released.markdown b/_i18n/cn/_posts/2015-05-08-frida-4-0-0-released.markdown new file mode 100644 index 00000000..d15bbf9a --- /dev/null +++ b/_i18n/cn/_posts/2015-05-08-frida-4-0-0-released.markdown @@ -0,0 +1,157 @@ +--- +layout: news_item +title: 'Frida 4.0.0 发布' +date: 2015-05-08 23:00:00 +0100 +author: oleavr +version: 4.0.0 +categories: [release] +--- + +是时候发布一个包含大量改进的疯狂版本了。 + +让我们从一个面向用户的更改开始。名为 *frida-repl* 的 CLI 工具已重命名为 *frida*,现在可以进行 tab 补全!这个和其他一些很棒的 REPL 好东西是由 [@fitblip](https://github.com/fitblip) 贡献的。 + +还有对直接从 shell 启动脚本的集成支持: + +{% highlight sh %} +$ frida Calculator -l calc.js + _____ + (_____) + | | Frida 4.0.0 - A world-class dynamic + | | instrumentation framework + |`-'| + | | Commands: + | | help -> Displays the help system + | | object? -> Display information about 'object' + | | exit/quit -> Exit + | | + | | More info at https://frida.re/docs/home/ + `._.' + +# The code in calc.js has now been loaded and executed +[Local::ProcName::Calculator]-> +# Reload it from file at any time +[Local::ProcName::Calculator]-> %reload +[Local::ProcName::Calculator]-> +{% endhighlight %} + +或者,也许您厌倦了 console.log() 并想在脚本中设置一些断点以帮助您了解发生了什么?现在您可以了,因为 Frida 刚刚获得了一个集成的 Node.js 兼容调试器。 + +(这里提示"Yo Dawg"模因。) + +是的,但它实际上非常有用,并且所有 CLI 工具都提供 `--debug` 开关来启用它: + +{% highlight bash %} +# Connect Frida to a locally-running Calculator.app +# and load calc.js with the debugger enabled +$ frida Calculator -l calc.js --debug + _____ + (_____) + | | Frida 4.0.0 - A world-class dynamic + | | instrumentation framework + |`-'| + | | Commands: + | | help -> Displays the help system + | | object? -> Display information about 'object' + | | exit/quit -> Exit + | | + | | More info at https://frida.re/docs/home/ + `._.' + +Debugger listening on port 5858 +# We can now run node-inspector and start debugging calc.js +[Local::ProcName::Calculator]-> +{% endhighlight %} + +它是这样的: + +![Frida Debugger Session](/img/frida-debug.png "Frida Debugger Session") + +有没有发现自己想直接从 shell *frida-trace* Objective-C API?感谢 [@Tyilo](https://github.com/Tyilo),您现在可以了: + +{% highlight bash %} +# Trace ObjC method calls in Safari +$ frida-trace -m '-[NSView drawRect:]' Safari +{% endhighlight %} + +还有其他好东西,比如对生成回溯和使用调试符号来符号化地址的全新支持: + +{% highlight js %} +const f = Module.getExportByName('libcommonCrypto.dylib', + 'CCCryptorCreate'); +Interceptor.attach(f, { + onEnter(args) { + console.log('CCCryptorCreate called from:\n' + + Thread.backtrace(this.context, Backtracer.ACCURATE) + .map(DebugSymbol.fromAddress).join('\n') + '\n'); + } +}); +{% endhighlight %} + +或者也许您在 Windows 上并试图弄清楚谁在访问某些内存区域?是吗?好吧,看看全新的 [MemoryAccessMonitor](/docs/javascript-api/#memoryaccessmonitor)。从技术上讲,这段代码并不新鲜,但直到现在它才暴露给 JavaScript API。 + +另一个不错的功能是,从这个版本开始,当使用在另一台设备(例如 Android)上运行的 `frida-server` 时,不再需要转发多个 TCP 端口。 + +现在还有更好的错误反馈,从远程进程一直传播到例如 Python 中的不同异常。在以前的版本中,在 Mac 上附加到不存在的 pid 会给您: + +{% highlight python %} +SystemError: GDBus.Error:org.gtk.GDBus.UnmappedGError.Quark._g_2↩ +dio_2derror_2dquark.Code0: task_for_pid() for remote pid failed w↩ +hile trying to make pipe endpoints: (os/kern) failure (5) +{% endhighlight %} + +哇,疯狂。现在这很简单: + +{% highlight python %} +frida.ProcessNotFoundError: unable to find process with pid 1234 +{% endhighlight %} + +好多了。让我们谈谈性能。也许您使用了 frida-trace 并想知道为什么它花了这么多时间"Resolving functions..."?在一个典型的 iOS 应用程序上,仅解析一个函数通常需要大约 8 秒。现在降到了约 1 秒。虽然有一些可能的优化,但我很快意识到,无论我们让函数导出的枚举有多快,我们仍然需要传输数据,而仅传输时间就可能是不合理的。解决方案?只需将逻辑移动到目标进程并传输逻辑而不是数据。简单。 + +此外,Dalvik 和 ObjC 接口已经过优化,因此秒数已减少到毫秒数。这里的简短故事是在我们询问语言运行时时进一步懒惰。我们在 ObjC 接口中走得很远,我们现在使用 ES6 代理来提供更惯用和高效的 API。 + +这将我们带到下一个主题。ObjC 接口发生了一些变化。本质上: + +{% highlight js %} +const NSString = ObjC.use("NSString"); +{% endhighlight %} + +现在是: + +{% highlight js %} +const NSString = ObjC.classes.NSString; +{% endhighlight %} + +您仍然使用 `ObjC.classes` 来枚举当前加载的类,但这现在的行为就像一个将类名映射到 JavaScript ObjC 绑定的对象。 + +此外,不再有转换,所以代替: + +{% highlight js %} +const NSSound = ObjC.use('NSSound'); +const sound = ObjC.cast(ptr("0x1234"), NSSound); +{% endhighlight %} + +您只需: + +{% highlight js %} +const sound = new ObjC.Object(ptr("0x1234")); +{% endhighlight %} + +是的,不再有类层次结构试图模仿 ObjC 的类层次结构。只是一个完全动态的包装器,其中方法包装器是在第一次访问时构建的,除非您尝试枚举对象的属性,否则不会获取方法列表。 + +无论如何,这变得很长,所以让我们总结一下其他关键变化: + +- Dalvik 接口现在处理可变参数方法。感谢 [@dmchell](https://github.com/dmchell) 报告并帮助追踪此问题。 +- *NativePointer* 还提供 `.and()`、`.or()` 和 `.xor()`,感谢 [@Tyilo](https://github.com/Tyilo)。 +- Interceptor 的 *onEnter*/*onLeave* 回调过去通过 `this.registers` 公开 CPU 寄存器,该寄存器已重命名为 `this.context`,现在也允许您写入寄存器。 +- *Process.enumerateThreads()* 的线程对象的 CPU 上下文字段从 `registers` 重命名为 `context` 以保持一致性。 +- enumerateFoo() API 的同步版本可用作 enumerateFoo**Sync**() 方法,该方法只需返回包含所有项目的数组。 +- `Memory.readCString()` 现在可用于读取 ASCII C 字符串。 +- 可以查询 `Frida.version` 以检查您正在运行的版本,这也在 *frida-core* 端提供,例如由 *frida-python* 通过 `frida.__version__` 公开。 +- *Stalker* 现在支持 *jecxz* 和 *jrcxz* 指令。这对 [CryptoShark](https://github.com/frida/cryptoshark) 来说是个好消息,它应该很快提供一些更新的二进制文件来捆绑最新版本的 Frida。 +- V8 已更新至 4.3.62,并且已启用许多 ES6 功能。 +- 我们现在使用的是即将推出的 Capstone 4.0 的开发版本。 +- 所有第三方依赖项已更新到最新和最好的版本。 +- 现在支持 Windows XP。这不是玩笑。我意识到我们实际上没有使用任何 XP 后的 API,而且由于我必须在 Windows 上重建依赖项,我想我们不妨降低我们的操作系统要求,以帮助那些仍在 XP 上插桩软件的人。 + +享受吧! diff --git a/_i18n/cn/_posts/2015-06-09-frida-4-1-released.markdown b/_i18n/cn/_posts/2015-06-09-frida-4-1-released.markdown new file mode 100644 index 00000000..668d6f89 --- /dev/null +++ b/_i18n/cn/_posts/2015-06-09-frida-4-1-released.markdown @@ -0,0 +1,253 @@ +--- +layout: news_item +title: 'Frida 4.1 发布' +date: 2015-06-09 09:00:00 +0100 +author: oleavr +version: 4.1 +categories: [release] +--- + +现在是发布时间,这次我们将 iOS 支持提升到一个新的水平,同时也带来了一些可靠的质量改进。我也非常兴奋地宣布,我最近加入了 [NowSecure](https://www.nowsecure.com/),这个版本的精彩绝非巧合。 + +让我们从一个全新的 iOS 功能开始。现在可以列出已安装的应用程序,*frida-ps* 可以为您做到这一点: + +{% highlight sh %} +$ frida-ps -U -a + PID NAME IDENTIFIER +10582 Facebook com.facebook.Facebook +11066 IRCCloud com.irccloud.IRCCloud + 451 Mail com.apple.mobilemail +10339 Mailbox com.orchestra.v2 + 6866 Messages com.apple.MobileSMS +10626 Messenger com.facebook.Messenger +11043 Settings com.apple.Preferences +10542 Skype com.skype.skype +11218 Slack com.tinyspeck.chatlyio +11052 Snapchat com.toyopagroup.picaboo +$ +{% endhighlight %} + +添加 `-i` 开关,它还将包括所有已安装的应用程序,而不仅仅是当前正在运行的应用程序。 + +这也适用于您选择的语言绑定,例如从 Python: + +{% highlight python %} +>>> import frida +>>> iphone = frida.get_usb_device() +>>> print("\n".join(map(repr, iphone.enumerate_applications()))) +Application(identifier="com.google.ios.youtube", name="YouTube") +Application(identifier="com.toyopagroup.picaboo", name="Snapchat") +Application(identifier="com.skype.skype", name="Skype", pid=10542) +… +>>> +{% endhighlight %} + +这很酷,但是您不想对这些应用程序进行早期插桩吗?现在您也可以这样做,只需让我们 spawn 一个应用程序标识符: + +{% highlight sh %} +$ frida-trace -U -f com.toyopagroup.picaboo -I "libcommonCrypto*" +{% endhighlight %} + +或者在 API 级别: + +{% highlight python %} +>>> import frida +>>> iphone = frida.get_usb_device() +>>> pid = iphone.spawn(["com.toyopagroup.picaboo"]) +>>> snapchat = iphone.attach(pid) +>>> …apply instrumentation… +>>> iphone.resume(pid) +{% endhighlight %} + +请注意,为了最大化互操作性,我们在早期启动部分搭载了 *Cydia Substrate*;毕竟如果多个框架都向 *launchd* 注入代码并冒着互相踩踏的风险,那就不太好了。然而,这种依赖关系是软依赖,因此如果在尝试使用应用程序标识符调用 `spawn()` 时未安装 Substrate,我们将抛出异常。 + +所以,iOS 应用程序的早期检测非常酷。但是,这些应用程序通常消耗大量的 Objective-C API,如果我们想检测它们,我们经常发现自己不得不创建新的 Objective-C 类,以便在应用程序和 API 之间插入委托。如果这样的 Objective-C 类可以用纯 JavaScript 创建,那不是很好吗?现在它们可以了: + +{% highlight js %} +const MyConnectionDelegateProxy = ObjC.registerClass({ + name: 'MyConnectionDelegateProxy', + super: ObjC.classes.NSObject, + protocols: [ObjC.protocols.NSURLConnectionDataDelegate], + methods: { + '- init': function () { + const self = this.super.init(); + if (self !== null) { + ObjC.bind(self, { + foo: 1234 + }); + } + return self; + }, + '- dealloc': function () { + ObjC.unbind(this.self); + this.super.dealloc(); + }, + '- connection:didReceiveResponse:': function (conn, resp) { + /* this.data.foo === 1234 */ + }, + /* + * But those previous methods are declared assuming that + * either the super-class or a protocol we conform to has + * the same method so we can grab its type information. + * However, if that's not the case, you would write it + * like this: + */ + '- connection:didReceiveResponse:': { + retType: 'void', + argTypes: ['object', 'object'], + implementation(conn, resp) { + } + }, + /* Or grab it from an existing class: */ + '- connection:didReceiveResponse:': { + types: ObjC.classes + .Foo['- connection:didReceiveResponse:'].types, + implementation(conn, resp) { + } + }, + /* Or from an existing protocol: */ + '- connection:didReceiveResponse:': { + types: ObjC.protocols.NSURLConnectionDataDelegate + .methods['- connection:didReceiveResponse:'].types, + implementation(conn, resp) { + } + }, + /* Or write the signature by hand if you really want to: */ + '- connection:didReceiveResponse:': { + types: 'v32@0:8@16@24', + implementation(conn, resp) { + } + } + } +}); + +const proxy = MyConnectionDelegateProxy.alloc().init(); +/* use `proxy`, and later: */ +proxy.release(); +{% endhighlight %} + +虽然大多数时候您想构建一个代理对象,在该对象中您传递所有内容,并且只为您真正关心的少数方法做一些日志记录。看看这个: + +{% highlight js %} +const MyConnectionDelegateProxy = ObjC.registerProxy({ + protocols: [ObjC.protocols.NSURLConnectionDataDelegate], + methods: { + '- connection:didReceiveResponse:': function (conn, resp) { + /* fancy logging code here */ + /* this.data.foo === 1234 */ + this.data.target + .connection_didReceiveResponse_(conn, resp); + }, + '- connection:didReceiveData:': function (conn, data) { + /* other logging code here */ + this.data.target + .connection_didReceiveData_(conn, data); + } + }, + events: { + forward(name) { + console.log('*** forwarding: ' + name); + } + } +}); + +const method = ObjC.classes.NSURLConnection[ + '- initWithRequest:delegate:startImmediately:']; +Interceptor.attach(method.implementation, { + onEnter(args) { + args[3] = new MyConnectionDelegateProxy(args[3], { + foo: 1234 + }); + } +}); +{% endhighlight %} + +这就是 Objective-C。感谢 [@marc1006](https://github.com/marc1006),Dalvik 集成也获得了一些用于枚举已加载类的甜蜜新 API,他还修复了我们对静态方法的处理,并且能够从重写的实现中返回布尔值。 + +我们还从 [@Tyilo](https://github.com/Tyilo) 那里得到了很多很棒的改进,他帮助改进了 ObjC 集成,将 REPL 打磨成更好的形状,添加了用于枚举 malloc 范围的 API,并向 *NativePointer* 添加了一些便捷 API。 + +在所有这些进行的同时,[@s1341](https://github.com/s1341) 一直在努力做着惊人的工作,将 Frida 移植到 QNX,现在它真的像魅力一样工作。 + +让我们浏览一下剩余的更改: + +4.0.1: + +- objc: 支持更多类型 +- frida-trace: 修复 ObjC 跟踪回归 + +4.0.2: + +- frida-node: 修复 *pixels* 属性的编码 + +4.0.3: + +- frida-repl: 修复 Windows 回归 + +4.0.5: + +- objc: 支持更多类型和更好的类型检查 +- objc: arm64 现在正常工作 +- frida-repl: 允许创建变量 + +4.0.6: + +- platform: 支持向 *send()* 传递纯数据数组 +- arm: 支持重定位 *cbz*/*cbnz* 指令 + +4.1.0: + +- platform: 修复写入 stdout 的子进程的生成 +- platform: 修复 NativeCallback 对 *bool*/*int8*/*uint8* 返回值的处理(这阻止了 Dalvik 方法重写能够返回 *false*)。 +- platform: 允许长度 < 1 的 *Memory.readByteArray()* +- arm: 支持重定位 *ldrpc t2* 指令 +- arm: 改进的重定向解析器 +- arm64: 修复 *adrp* 指令的重定位 +- arm64: 支持重定位 PC 相对 *ldr* 指令 +- dalvik: 添加 *Dalvik.enumerateLoadedClasses()* +- dalvik: 修复静态方法的处理 +- python: 修复 Windows 上的 *console.log()* +- frida-repl: 错误修复和改进 +- frida-trace: 对跟踪 ObjC 方法的 glob 支持 + +4.1.1: + +- platform: 在 *enumerate_applications()* 中添加缺少的 pid 字段 + +4.1.2: + +- objc: 类和代理创建 API +- objc: 用于枚举协议的新 *ObjC.protocols* API + +4.1.3: + +- platform: 通过在调用 NativeFunction 时释放 V8 锁来改进并发性 +- platform: 添加 *Process.getModuleByName(name)* +- platform: 更快更健壮的分离 +- python: CLI 工具的稳定性改进 +- frida-repl: 用 *prompt-toolkit* 替换 *readline* + +4.1.4: + +- platform: 更快更健壮的拆卸 +- frida-server: 在 *SIGINT* 和 *SIGTERM* 上清理 + +4.1.5: + +- frida-ps: 添加对列出应用程序的支持 + +4.1.6: + +- platform: 修复 Mac、iOS 和 Linux 上生成时的崩溃 +- platform: 添加 *NativePointer.compare()* 和 *NativePointer.equals()* +- platform: 添加 *Process.enumerateMallocRanges{,Sync}()* +- frida-trace: 从 Enter 切换到 Ctrl+C 停止 +- frida-trace: 修复 iOS 应用程序的生成 +- frida-repl: 向自动完成添加原型名称 + +4.1.7: + +- python: CLI 工具稳定性改进 + +目前就这些。请通过在网络上传播这篇文章来帮助宣传。作为一个开源项目,我们还很小,所以口碑营销对我们来说意义重大。 + +享受吧! diff --git a/_i18n/cn/_posts/2015-06-18-frida-4-2-released.markdown b/_i18n/cn/_posts/2015-06-18-frida-4-2-released.markdown new file mode 100644 index 00000000..11640577 --- /dev/null +++ b/_i18n/cn/_posts/2015-06-18-frida-4-2-released.markdown @@ -0,0 +1,153 @@ +--- +layout: news_item +title: 'Frida 4.2 发布' +date: 2015-06-18 19:00:00 +0100 +author: oleavr +version: 4.2 +categories: [release] +--- + +Frida 的同谋者们最近一直在几条战线上努力工作,以至于我觉得值得记下来把消息传出去。 + +在 Dalvik 领域,[@marc1006](https://github.com/marc1006) 贡献了一个非常整洁的新功能 —— 对象雕刻的能力,本质上是扫描堆中特定类型的对象。看看这个: + +{% highlight js %} +const strings = []; +Dalvik.choose('java.lang.String', { + onMatch(str) { + strings.push(str); + }, + onComplete() { + console.log('Found ' + strings.length + ' strings!'); + } +}); +{% endhighlight %} + +与此同时,[@Tyilo](https://github.com/Tyilo) 一直在为 Objective-C 添加相同的功能: + +{% highlight js %} +const strings = []; +ObjC.choose(ObjC.classes.NSString, { + onMatch(str) { + strings.push(str); + }, + onComplete() { + console.log('Found ' + strings.length + ' strings!'); + } +}); +{% endhighlight %} + +在其他移动新闻中,[@pancake](https://github.com/trufae) 添加了对枚举 Firefox OS 上的应用程序的支持。太棒了! + +在所有这些进行的同时,[@s1341](https://github.com/s1341) 一直在努力稳定 QNX 端口,据报道它现在运行得非常好。 + +在我这边,我一直在 [NowSecure](https://www.nowsecure.com/) 将 Frida 应用于有趣的挑战,并在 Objective-C 集成中遇到了一些错误和限制。现在支持覆盖处理按值传递的结构类型的方法,例如 `-[UIView drawRect:]`,这意味着 `NativeFunction` 和 `NativeCallback` 也支持这些;因此,要声明结构,只需启动一个数组,其中按顺序指定字段的类型。您甚至可以嵌套它们。因此,对于按值传递结构且该结构由另外两个结构组成的 `- drawRect:` 案例,您可以像这样声明它: +- core: 添加 *NativePointer.toMatchPattern()* 以与 *Memory.scan()* 一起使用 +- core: 修复 QNX 注入器竞争条件 +- objc: 大幅改进类型的处理 +- objc: 修复从 JS 字符串到 NSString 的隐式转换 +- objc: 修复注册第二个代理或未命名类时的崩溃 +- objc: 新的 *ObjC.Object* 属性: *$className* 和 *$super* +- dalvik: 添加 *Dalvik.choose()* 用于对象雕刻 + +4.1.9: + +- core: *NativeFunction* 和 *NativeCallback* 现在支持按值传递结构类型的函数 +- core: 修复 *Process.getModuleByName()* 中的意外大小写敏感性 +- dalvik: 新的对象属性: *$className* + +4.2.0: + +- core: 向 *Interceptor* 的 *onEnter* 和 *onLeave* 回调添加 *this.returnAddress* +- objc: 添加 *ObjC.choose()* 用于对象雕刻 + +4.2.1: + +- core: 修复 QNX 上剥离库的导出枚举 +- objc: 新的 *ObjC.Object* 属性: *$kind*,一个字符串,是 *instance*、*class* 或 *meta-class* +- objc: 修复 *$class* 属性,使其对类也做正确的事情 +- objc: 修复查找不存在的方法时的崩溃 +- python: 确保反应器线程的优雅拆卸 +- frida-discover: 修复回归 +- frida-repl: 修复目标在评估表达式期间崩溃时的挂起 + +4.2.2: + +- core: 修复异常处理的怪异现象;在 ios-arm 上非常明显 +- core: QNX 稳定性改进 +- objc: 添加 *ObjC.api* 以直接访问 Objective-C 运行时的 API +- objc: 新的 *ObjC.Object* 属性: *equals*、*$superClass* 和 *$methods* +- objc: 修复 iOS 7 兼容性 +- objc: 修复 *ObjC.classes* 和 *ObjC.protocols* 的 *toJSON()* +- dalvik: 修复 *java.lang.CharSequence* 的处理 +- frida-repl: 添加 *%time* 命令以便于分析 + +4.2.3: + +- core: 修复处理没有消息对象的异常时的崩溃 +- core: 修复 CpuContext JS 包装器的生命周期 +- core: 向 *Process.enumerateRanges()* 公开文件映射信息 +- core: 使枚举时合并相邻范围成为可能 +- core: 添加用于查找模块和范围的便捷 API +- core: 使 QNX mprotect 在循环中读取而不是只读取一次 +- dalvik: 如果类型转换失败,避免使进程崩溃 +- dalvik: 允许 *null* 作为调用参数 +- objc: 修复具有简单字段类型的结构的转换 +- objc: 通过缓存包装对象加速隐式字符串转换 + +4.2.4: + +- objc: 修复与尚未实现的类交互时的崩溃 + +4.2.5: + +- core: 优化 Interceptor 回调逻辑,当未同时指定 *onEnter* 和 *onLeave* 时使其快两倍 +- core: 修复 arm64 上调用上下文看到的返回地址 +- core: 为 arm64 添加模糊回溯器 + +4.2.6: + +- core: 修复 arm64 上对参数 4 到 7 的访问 +- core: 添加 *Memory.readFloat()*、*Memory.writeFloat()*、*Memory.readDouble()* 和 *Memory.writeDouble()* +- dalvik: 改进类型检查 +- qnx: 实现侧堆栈,用于使用消耗堆栈的 V8 引擎调用 *onEnter()*/*onLeave()* + +4.2.7: + +- core: Darwin 后端错误修复 +- core: 优化 *send()* 数据有效载荷的处理 +- core: 添加通过 *task_for_pid(0)* 与 iOS 内核交互的 API,仅在 *attach(pid=0)* 会话中可用 +- core: QNX 上替换函数的侧堆栈支持 +- objc: 向 ObjC.classes 添加 *getOwnPropertyNames()* +- frida-repl: 改进完成 + +4.2.8: + +- python: 修复 Py3k 回归 + +4.2.9: + +- objc: 向 *ObjC.Object* 添加 *$ownMethods* +- dalvik: 添加对原始数组和对象数组的支持 +- python: 改进 Python 2 和 3 之间的兼容性 +- frida-repl: 更好的魔术命令 + +4.2.10: + +- core: 修复 arm64 上 Interceptor 向量寄存器破坏问题 +- core: 改进 Android 上的临时目录处理 + +4.2.11: + +- dalvik: 添加对访问实例和静态字段的支持 +- dalvik: 类型转换改进 +- python: 在 Mac 上延迟解析 python 运行时,以允许我们的二进制文件与多个 Python 发行版一起工作 +- python: pip 支持 + +4.2.12: + +- python: 修复 Py3k 回归 + +目前就这些。请通过在网络上传播这篇文章来帮助宣传。作为一个开源项目,我们还很小,所以口碑营销对我们来说意义重大。 + +享受吧! diff --git a/_i18n/cn/_posts/2015-07-15-frida-4-3-released.markdown b/_i18n/cn/_posts/2015-07-15-frida-4-3-released.markdown new file mode 100644 index 00000000..6d434c3c --- /dev/null +++ b/_i18n/cn/_posts/2015-07-15-frida-4-3-released.markdown @@ -0,0 +1,58 @@ +--- +layout: news_item +title: 'Frida 4.3 发布' +date: 2015-07-15 19:00:00 +0100 +author: oleavr +version: 4.3 +categories: [release] +--- + +现在是发布时间,这次我们在各处都有大量的改进。简而言之: + +4.3.0: + +- core: 添加对获取有关最前端应用程序详细信息的支持,最初仅适用于 iOS +- python: 添加 *Device.get_frontmost_application()* +- node: 添加 *Device.getFrontmostApplication()* + +4.3.1: + +- core: 添加对在 arm64 上重定位 PC 相对 *CBZ* 的支持 +- frida-repl: 修复 Py3k 上的崩溃和脚本加载 + +4.3.2: + +- core: 添加对使用 URL 启动 iOS 应用程序的支持 +- dalvik: 修复字段缓存中的错误 +- frida-trace: 根据线程 ID 和深度对事件进行着色和缩进 +- frida-ps: 修复 Py3k 上的应用程序列表 + +4.3.3: + +- core: 在意外禁用 Darwin 映射器后重新启用它 + +4.3.4: + +- core: 优雅地处理替换函数的尝试 +- core: 当 Interceptor 的 *attach()* 和 *replace()* 失败时抛出异常 +- core: 修复 agent 会话的清理 +- core: 修复断言日志记录并在 Darwin 上记录到 CFLog +- dalvik: 添加 *Dalvik.synchronized()*、*Dalvik.scheduleOnMainThread()* 和 *Dalvik.isMainThread()* +- dalvik: 将 *Dalvik.androidVersion* 和 *Dalvik.choose()* 移植到 Android 4.2.2 +- python: 修复 windows-i386 的 PyPI 下载 URL +- frida-trace: 优雅地处理 *attach()* 失败 + +4.3.5: + +- frida-server: 更好的资源跟踪 + +4.3.6: + +- core: 修复 arm64 函数 hook +- dalvik: 修复 *Dalvik.enumerateLoadedClasses()* + +4.3.7: + +- objc: 添加 *ObjC.Block* 用于实现和与块交互 + +享受吧! diff --git a/_i18n/cn/_posts/2015-07-31-frida-4-4-released.markdown b/_i18n/cn/_posts/2015-07-31-frida-4-4-released.markdown new file mode 100644 index 00000000..61c96645 --- /dev/null +++ b/_i18n/cn/_posts/2015-07-31-frida-4-4-released.markdown @@ -0,0 +1,20 @@ +--- +layout: news_item +title: 'Frida 4.4 发布' +date: 2015-07-31 19:00:00 +0100 +author: oleavr +version: 4.4 +categories: [release] +--- + +随着 4.4 的发布,我们现在可以为您提供全新的 [RPC API](/docs/javascript-api/#rpc),使与脚本通信并让它们向您的应用程序公开服务变得超级容易。我们还从 [Adam Brady](https://github.com/SomeoneWeird) 那里获得了一些惊人的贡献,他刚刚将 frida-node 移植到 [Nan](https://github.com/nodejs/nan),使其易于为多个版本的 Node.js 构建。 + +总结一下这个版本: + +- core: 添加新的 RPC API +- python: 添加对调用 RPC 导出的支持 +- node: 添加对调用 RPC 导出的支持 +- node: 允许发布的消息值是任何可序列化为 JSON 的东西 +- node: 移植到 Nan + +享受吧! diff --git a/_i18n/cn/_posts/2015-09-10-frida-4-5-released.markdown b/_i18n/cn/_posts/2015-09-10-frida-4-5-released.markdown new file mode 100644 index 00000000..795bcd16 --- /dev/null +++ b/_i18n/cn/_posts/2015-09-10-frida-4-5-released.markdown @@ -0,0 +1,45 @@ +--- +layout: news_item +title: 'Frida 4.5 发布' +date: 2015-09-10 19:00:00 +0100 +author: oleavr +version: 4.5 +categories: [release] +--- + +是时候发布另一个打包版本了。这次我们带来了全新的 spawn gating API,让您可以捕获系统生成的进程,以及大量的 Android 改进和各处的改进。 + +因此,事不宜迟,更改列表如下: + +4.5.0: + +- core: 添加 *Process.pageSize* 常量 +- core: 当 size >= page size 时,让 *Memory.alloc()* 分配原始页面 +- core: 修复 NativeFunction 对小返回类型的处理 +- core: 重写 BLX 指令时修复 PC 对齐 +- core: 添加 spawn gating API +- core: 在 Android 上实现 *get_frontmost_application()* +- core: 在 Android 上实现 *enumerate_applications()* +- core: 添加对启动 Android 应用程序的支持 +- core: 添加对注入 Android 上 arm64 进程的支持 +- core: 添加对 Android M 的支持 +- core: 修补内核的实时 SELinux 策略 +- core: 与 SuperSU 集成以解决 Samsung 内核上的限制 +- core: 解决 Android 上损坏的 sigsetjmp,以及许多其他 Android 修复 +- core: 修复 Linux 上枚举模块时的崩溃 +- core: 优化 Darwin 上远程进程的导出枚举 +- dalvik: 移植到 ART 并弃用 *Dalvik* 名称,现在称为 *Java* +- java: 添加 *Java.openClassFile()* 以允许在运行时加载类 +- java: 修复数组转换和字段设置器 +- python: 添加对新 spawn gating API 的支持 +- python: 允许脚本源和名称在 Python 2.x 上也是 unicode +- python: 修复 Python 3.x 中的错误传播 +- python: 修复 Linux 下载 URL 计算 +- node: 添加对新 spawn gating API 的支持 +- node: 移植到 Nan 2.x + +4.5.1: + +- core: 修复 `ensure_host_session()` 错误传播 + +享受吧! diff --git a/_i18n/cn/_posts/2015-09-17-frida-5-0-released.markdown b/_i18n/cn/_posts/2015-09-17-frida-5-0-released.markdown new file mode 100644 index 00000000..9f897fac --- /dev/null +++ b/_i18n/cn/_posts/2015-09-17-frida-5-0-released.markdown @@ -0,0 +1,95 @@ +--- +layout: news_item +title: 'Frida 5.0 发布' +date: 2015-09-17 19:00:00 +0100 +author: oleavr +version: 5.0 +categories: [release] +--- + +哇,又一个主要版本!我们决定更改 Device API 以为您提供持久 ID,以便您可以轻松区分热插拔时的不同设备。 + +但这仅仅是开始,这次我们还带来了大量其他改进: + +5.0.0: + +- core: 更改 Device.id 以表示跨重新连接的单个设备 +- core: 添加新的 Droidy 后端用于与连接的 Android 设备接口 +- core: 调整 Darwin 上令人困惑的 iPhone 5+ 设备名称 +- core: 规范化回退 iOS 设备名称以与 Android 保持一致 +- core: 将 V8 升级到 4.5.103.30 +- objc: 在 *$methods* 和 *$ownMethods* 中包含类和实例方法 +- python: 添加 -D 开关用于指定要连接的设备 ID +- python: 添加 frida-ls-devices CLI 工具用于列出设备 +- python: 更新到新的 Device.id API +- python: 添加 *get_local_device()* 并提高与 frida-node 的 API 一致性 +- node: 更新到新的 Device.id API +- node: 改进顶级外观 API +- qml: 更新到新的 Device.id API +- clr: 更新到新的 Device.id API +- frida-ps: 改进输出格式 + +5.0.1: + +- core: 添加对源映射的支持 +- node: 添加 frida.load() 用于将 CommonJS 模块转换为脚本 +- node: 升级 Nan + +5.0.2: + +- core: 添加 *console.warn()* 和 *console.error()* +- core: 添加 *Module.enumerateImports()* 并在 Darwin、Linux 和 Windows 上实现 +- core: 调用 *Module.findExportByName()* 时允许 *null* 模块名称 +- core: 将 *Darwin.Module* 和 *Darwin.Mapper* 从 frida-core 移动到 frida-gum,允许轻松的 Mach-O 解析和进程外动态链接 +- core: 更好地处理临时文件 +- frida-trace: 添加对方便跟踪导入函数的支持 +- frida-trace: 将 dyld_stub_binder 列入黑名单以防止被跟踪 +- python: 避免日志被状态消息更改覆盖 + +5.0.3: + +- core: 改进 arm64 hook,包括对 hook 短函数的支持 + +5.0.4: + +- core: 改进 arm64 hook,还要注意避免重定位其他指令依赖的指令,包括 BL/BLR/SVC 指令之后的下一条指令 +- core: 将 *Arm64Writer* 和 *Arm64Relocator* 移植到 Capstone + +5.0.5: + +- core: 通过使用我们的 GLib 补丁提供的新 API 修复拆卸时的崩溃 +- core: 修复 Linux 上的模块名称解析 +- core: 改进 ELF 处理以也将 *ET_EXEC* 映像视为有效模块 +- core: 改进 arm64 hook +- core: 将 *{Arm,Thumb}Writer* 和 *{Arm,Thumb}Relocator* 移植到 Capstone +- python: 修复 OS X 10.11 上的测试 +- node: 修复 OS X 10.11 上的测试 + +5.0.6: + +- core: 尽可能将 NativeFunction 调用崩溃转换为 JS 异常 +- core: 添加 *Process.setExceptionHandler()* 用于处理来自 JS 的本机异常 +- core: 安装一个发出错误消息的默认异常处理程序 +- core: 如果我们在进程生命周期的早期安装我们的异常处理程序,则防止应用程序覆盖我们的异常处理程序 +- core: 如果我们无法替换本机函数,则优雅地处理它 +- core: 允许 RPC 导出返回 ArrayBuffer 值 +- python: 添加对返回 ArrayBuffer 对象的 rpc 方法的支持 +- node: 添加对返回 ArrayBuffer 对象的 rpc 方法的支持 + +5.0.7: + +- core: 暂时不安装默认异常处理程序 + +5.0.8: + +- 由于构建机器问题,重新发布 5.0.7。 + +5.0.9: + +- python: 更新 setup.py 以匹配新的构建服务器配置 + +5.0.10: + +- core: 修复早期使用 IP 寄存器的 arm64 函数的检测 + +享受吧! diff --git a/_i18n/cn/_posts/2015-11-11-frida-6-0-released.markdown b/_i18n/cn/_posts/2015-11-11-frida-6-0-released.markdown new file mode 100644 index 00000000..e3eb47eb --- /dev/null +++ b/_i18n/cn/_posts/2015-11-11-frida-6-0-released.markdown @@ -0,0 +1,110 @@ +--- +layout: news_item +title: 'Frida 6.0 发布' +date: 2015-11-11 19:00:00 +0100 +author: oleavr +version: 6.0 +categories: [release] +--- + +这次是史诗般的发布,带来了全新的 iOS 9 支持和各处的改进。有关更多背景信息,请查看我的博客文章 [here](https://www.nowsecure.com/blog/2015/11/16/ios-9-reverse-engineering-with-javascript/) 和 [here](https://www.nowsecure.com/blog/2015/11/23/ios-instrumentation-without-jailbreak/)。 + +这里有很多内容要涵盖,但摘要基本上是: + +6.0.0: + +- core: 添加对 OS X El Capitan 的支持 +- core: 添加对 iOS 9 的支持 +- core: 修复 Cydia 包中的 launchd plist 权限 +- core: 暂时在 iOS 上禁用我们的动态链接器 +- core: 添加基于 JavaScriptCore 的新 JavaScript 运行时,因为我们无法在当前的越狱中使用 V8 在 iOS 9 上 +- core: 当附加到 *pid=0* 时添加全新的系统会话 +- core: 改进 arm hook,包括对早期 TBZ/TBNZ/IT/B.cond 的支持,并避免重定位后续指令循环回的指令 +- core: 修复 arm64 上 LDR.W 指令的重定位 +- core: 当我们陷入异常循环时中止 +- core: 修复 *AutoIgnorer* 相关的死锁 +- core: 删除我们的 *.* 前缀,以便更容易发现临时文件 +- python: 添加对在没有 ES6 支持的情况下运行的支持 +- python: 调整 setup.py 以允许离线安装 +- python: 暂时将 prompt-toolkit 版本锁定为 0.38 +- frida-repl: 修复 *Memory.readByteArray()* 返回的原始缓冲区的显示 +- frida-repl: 修复错误完成时的崩溃 +- node: 添加对 DeviceManager 的 *added* 和 *removed* 信号的支持 +- node: 添加示例,显示如何监视可用设备 +- node: 使用 prebuild 而不是 node-pre-gyp +- node: Babelify *frida.load()* 读取的源代码 +- node: 删除 *frida.load()*,因为它现在在 frida-load 模块中 + +6.0.1: + +- python: 停止提供 3.4 二进制文件,改为移动到 3.5 +- node: 修复 Linux 链接问题,即我们未能获取 libffi +- node: 也为 Node.js LTS 生成预构建 + +6.0.2: + +- core: 提供 FridaGadget.dylib 用于在没有越狱的情况下检测 iOS 应用程序 +- core: 添加对 iOS 模拟器的支持 +- core: 改进 *MemoryAccessMonitor* 以允许监视页面上 R、W 或 X 操作的任何组合 +- python: 修复 UTF-8 字段在 Python 2.x 上意外暴露为 *str* + +6.0.3: + +- core: 修复 OS X 上的 *spawn()* + +6.0.4: + +- core: 添加对独立使用 gadget 的部分支持 +- CLI tools: 修复 stdout 编码无法表示所有字符时的崩溃 +- frida-trace: 始终将处理程序脚本视为 UTF-8 + +6.0.5: + +- core: 向 NativePointer 添加逻辑右移和左移操作 +- core: 改进 Interceptor 以支持附加到已替换的函数 +- core: 添加对在 32 位 ARM 上 hook 微小函数的支持 +- core: 在 Windows 上模拟 *{Get/Set}LastErrror* 和 TLS 密钥访问,允许我们 hook 更多低级 API + +6.0.6: + +- core: 修复 iOS 9 上的 launchd / Jetsam 问题 +- core: 修复 iOS 9 代码签名问题 +- core: 更新命名管道上的安全属性,以允许我们注入更多 Windows 应用程序 + +6.0.7: + +- core: 添加对注入 linux-arm 上进程的支持 +- core: 修复 Mac 和 iOS 上与 DebugSymbol API 相关的崩溃 +- frida-trace: 改进手册页解析器 + +6.0.8: + +- core: 修复由于未能静态链接 libstdc++ 而导致的 Linux 兼容性问题 + +6.0.9: + +- core: 添加对独立运行 frida-gadget 的支持 +- core: 为 Windows 兼容性回归添加临时解决方法 +- core: 将 Fruity 后端移植到 Linux,允许直接访问连接的 iOS 设备 +- core: 在 JavaScriptCore 运行时中也公开 InvocationContext *context* 读写 +- core: 修复 InvocationContext 的 CpuContext 过早被 GC 的问题 + +6.0.10: + +- 重新发布 6.0.9,修复了 Windows 构建回归。 + +6.0.11: + +- core: 防止在网络错误的情况下出现陈旧的 HostSession 对象 +- CLI tools: 当 stdout 编码未知时假设 UTF-8 +- node: 修复因使用错误的 Nan API 而导致的双重释放 + +6.0.12: + +- core: 更新 Windows 上命名管道的安全属性 +- core: 添加 CreateProcessW 标志以防止 Windows 上的 IFEO 循环 +- core: 修复 arm 和 arm64 上递归函数的 hook +- python: 修复 Python 3 行尾回归 +- node: 更新 prebuild 依赖项 + +享受吧! diff --git a/_i18n/cn/_posts/2016-01-14-frida-6-1-released.markdown b/_i18n/cn/_posts/2016-01-14-frida-6-1-released.markdown new file mode 100644 index 00000000..c9bad16f --- /dev/null +++ b/_i18n/cn/_posts/2016-01-14-frida-6-1-released.markdown @@ -0,0 +1,71 @@ +--- +layout: news_item +title: 'Frida 6.1 发布' +date: 2016-01-14 19:00:00 +0100 +author: oleavr +version: 6.1 +categories: [release] +--- + +前段时间 [@s1341](https://github.com/s1341) 将 Frida 移植到了 QNX,就在几周前,他在嵌入式 ARM 设备上使用 Frida 时遇到了内存占用问题。这就在他贡献了将 Frida 移植到 linux-arm 的拉取请求之后。我们开始意识到可能是时候使用新的 JavaScript 运行时了,并同意 [Duktape](http://duktape.org/) 似乎非常适合我们的需求。 + +这个运行时现在已经落地,所有测试都通过了,它甚至在调用带有空 *onEnter*/*onLeave* 回调的 hook 函数的测量开销上击败了我们的 V8 运行时。给您一个概念: + +{% highlight sh %} +…/interceptor_on_enter_performance: V8 min=2 max=31 avg=2 OK +…/interceptor_on_enter_performance: DUK min=1 max=2 avg=1 OK +{% endhighlight %} + +(数字以微秒为单位,在运行 OS X 10.11.2 的 4 GHz i7 上测量。) + +无论如何,即使那个比较并不完全公平,因为我们做了一些我们在 V8 运行时中尚未做的巧妙回收和写时复制技巧,这个新运行时已经相当令人印象深刻了。它还允许我们在非常微小的设备上运行,并且像 V8 这样咆哮的 JIT 驱动怪兽与纯解释器之间的性能差异对于大多数 Frida 用户来说可能并不重要。 + +因此,从这个版本开始,我们还在所有预构建的二进制文件中包含了这个全新的运行时,以便您可以试用它并告诉我们它对您的效果如何。它只增加了几百 KB 的占用空间,与 V8 每个架构切片增加的 6 MB 相比根本不算什么。请通过向 CLI 工具传递 `--disable-jit`,或者在第一次调用 `session.create_script()` 之前调用 `session.disable_jit()` 来试用它。 + +考虑到这个新运行时还解决了一些需要在我们的 JavaScriptCore 运行时中进行大量工作才能修复的问题,例如忽略来自后台线程的调用并避免毒害应用程序的堆,我们决定摆脱该运行时,并在 V8 目前无法运行的操作系统(如 iOS 9)上切换到这个基于 Duktape 的新运行时。我们在运行时进行功能检测,因此您仍然可以像以前一样在 iOS 8 上使用 V8 —— 除非您像刚才提到的那样显式 `--disable-jit`。 + +最后,这是更改的摘要: + +6.1.0: + +- core: 用基于 Duktape 构建的继任者替换 JavaScriptCore 运行时 +- core: 添加 *disable_jit()* 以允许用户试用新的 Duktape 引擎 +- core: 修复 Linux 上注入尚未调用/绑定 *pthread_create* 的进程时的崩溃 +- core: 添加对 linux-armhf (e.g. Raspberry Pi) 的支持 +- python: 向 Session 添加 *disable_jit()* +- node: 向 Session 添加 *disableJit()* +- CLI tools: 添加 *--disable-jit* 开关 +- frida-repl: 升级到最新的 prompt-toolkit +- frida-trace: 修复尝试跟踪部分解析的导入时的崩溃 +- frida-trace: 在生成的处理程序中坚持使用 ES5 以实现 Duktape 兼容性 + +6.1.1: + +- core: 修复 Duktape 运行时中的同步逻辑和错误处理错误 + +6.1.2: + +- core: 修复导致注入时崩溃的 Android 回归 +- core: 修复 Python 3.x 构建回归 +- clr: 向 Session 添加 *DisableJit()* + +6.1.3: + +- core: 赋予 iOS frida-helper 与 Preferences 应用程序相同的所有 entitlements,以便系统会话脚本可以读取和写入系统配置 +- core: 更改以支持其中临时目录/文件的 AppContainer ACL +- node: 修复 pid 检查,以便它允许附加到系统会话 + +6.1.4: + +- core: 为 iOS 上的控制台二进制文件实现 spawn() +- core: 改进对 hook 低级 OS API 的支持 +- core: 修复阻止我们注入 frida-agent 依赖的库尚未加载的 Mac 进程的映射器问题 +- core: 使 InvocationContext 也可用于替换的函数 + +6.1.5: + +- core: 在 frida-load 生成的脚本中添加对生成器函数的支持 +- frida-repl: 修复导致挂起的竞争条件 +- frida-repl: 修复退出时的虚假错误消息 + +享受吧! diff --git a/_i18n/cn/_posts/2016-01-31-frida-fosdem-presentation.markdown b/_i18n/cn/_posts/2016-01-31-frida-fosdem-presentation.markdown new file mode 100644 index 00000000..677e0ec8 --- /dev/null +++ b/_i18n/cn/_posts/2016-01-31-frida-fosdem-presentation.markdown @@ -0,0 +1,18 @@ +--- +layout: news_item +title: 'FOSDEM 2016 上的 Frida 演讲' +date: 2016-01-31 23:59:59 +0100 +author: karltk +categories: [presentation] +--- + +[oleavr][] 和 [karltk][] 昨天在 [FOSDEM][] [Testing and Automation][] 开发室介绍了 Frida。 + +我们的演讲[幻灯片][Slides]——题为 *Testing interoperability with closed-source software through scriptable diplomacy*——可在我们的[演示文稿][presentations]页面上找到。目前还没有演讲的视频。 + +[oleavr]: https://github.com/oleavr +[karltk]: https://github.com/karltk +[FOSDEM]: https://fosdem.org/2016 +[Testing and Automation]: https://fosdem.org/2016/schedule/track/testing_and_automation/ +[Slides]: {{ site.baseurl_root }}/slides/fosdem-2016-testing-interoperability-with-closed-source-software-through-scriptable-diplomacy.pdf +[presentations]: /docs/presentations/ diff --git a/_i18n/cn/_posts/2016-02-01-frida-6-2-released.markdown b/_i18n/cn/_posts/2016-02-01-frida-6-2-released.markdown new file mode 100644 index 00000000..18bdbab3 --- /dev/null +++ b/_i18n/cn/_posts/2016-02-01-frida-6-2-released.markdown @@ -0,0 +1,93 @@ +--- +layout: news_item +title: 'Frida 6.2 发布' +date: 2016-02-01 22:00:00 +0100 +author: oleavr +version: 6.2 +categories: [release] +--- + +现在是发布时间,这次我们为您带来了所有平台上的巨大性能改进、用于查找函数的全新 API 以及 iOS 9 上的重大稳定性改进。 + +让我们先谈谈后一个主题。你们中的一些人可能已经注意到在 iOS 9 上使用 Frida 时会出现奇怪的错误和死锁。根本原因很简单,我们的内联 hook 导致进程丢失其 [代码签名状态](https://github.com/frida/frida-gum/blob/ae22e0fa94970a9df140757e4aa0467e9deea9aa/tests/core/interceptor.c#L1111) 的 *CS_VALID* 位。这在 iOS 8 和更旧版本上不是问题,因为越狱总是能够修补内核以放宽其代码签名要求。从这个版本开始,我们实施了一些技巧,以便能够在不破坏代码签名状态的情况下进行内联 hook。对于技术好奇的人来说,这意味着我们动态生成一个 .dylib 作为临时文件,写出我们想要修改的内存页面的新版本,例如包含 *open()* 的 libc 内存页面,然后伪签名此二进制文件,要求内核 [F_ADDFILESIGS](https://github.com/frida/frida-gum/blob/ae22e0fa94970a9df140757e4aa0467e9deea9aa/gum/backend-darwin/gumcodesegment-darwin.c#L211) 给它,最后从这个文件 *mmap()* 到原始内存页面之上。 + +这将我们带到下一个主题:性能。我刚才谈到的技巧实际上确实增加了一些额外的开销,只是为了 hook 一个函数。这也是一种与我们在支持读写执行内存页面和宽松代码签名要求的系统上所能做的截然不同的方法,所以这显然意味着需要重大的架构更改。我也一直在考虑能够一次应用一整批 hook,使我们能够更有效率,并对 hook 何时激活有更多控制。 + +从这个版本开始,我们的 Interceptor API 现在支持 [事务](https://github.com/frida/frida-gum/blob/ae22e0fa94970a9df140757e4aa0467e9deea9aa/gum/guminterceptor.h#L78-L79)。只需调用 *begin_transaction()*,hook 所有函数,并通过调用 *end_transaction()* 一次性使它们全部处于活动状态。这导致了巨大的性能提升,并且您无需更改现有代码即可免费获得所有这些。这是因为我们在进入 JavaScript 运行时时隐式开始事务,并在离开时结束它(就在我们 *send()* 消息或从 RPC 方法返回之前)。因此,除非您从计时器或像 *Memory.scan()* 这样的异步 API 附加您的 hook,否则它们都将被分批处理到单个事务中并获得性能提升。 + +以下是我们在性能方面与 CydiaSubstrate 的对比: + +{% gist bfd9b65865e9f17914f2 %} + +请注意,如果您从 C 或 C++ 使用我们的插桩引擎,您将必须自己调用 *begin_transaction()* 和 *end_transaction()* 才能获得此提升,但即使您不这样做,您的代码仍然可以工作,因为每个操作都将隐式包含一个事务,并且 API 允许嵌套这些调用。 + +那是函数 hook 性能,但我们并没有止步于此。如果您曾经使用 *frida-trace* 跟踪 Objective-C API,或跨所有加载的库 glob 函数,您可能已经注意到解析所有函数可能需要相当长的时间。如果您将其与早期插桩相结合,它甚至可能花费太长时间以至于我们超过了系统的 [启动超时](https://github.com/frida/frida/issues/103)。所有这些现在都已优化,为了让您了解加速情况,以前需要几秒钟的典型 Objective-C 案例现在只需几毫秒即可完成。 + +现在是新闻的最后一部分。考虑到动态发现要 hook 的函数是如此常见的用例,而不仅仅是 *frida-trace* 所做的,我们现在有一个 [全新的 API](https://frida.re/docs/javascript-api/#apiresolver) 专门用于此: + +![ApiResolver #1](/img/api-resolver-module.png "ApiResolver") + +![ApiResolver #2](/img/api-resolver-objc.png "ApiResolver") + +最后,这是更改的摘要: + +6.2.0: + +- core: 改进 Interceptor 以避免破坏 iOS 9 上的动态代码签名 +- core: 迁移到基于事务的 Interceptor API 以提高性能 +- core: 修复计划回调被延迟释放时的崩溃 (V8 和 Duktape) +- frida-trace: 通过删除 *setTimeout()* 逻辑提高性能,允许在同一事务中应用许多 hook +- frida-trace: 以 50 毫秒的块批量处理日志事件以提高性能 + +6.2.1: + +- core: 添加 *ApiResolver* API +- frida-trace: 通过使用新的 *ApiResolver* API 提高性能 + +6.2.2: + +- core: 修复阻止注入 Windows Store/Universal 应用程序的错误 +- core: 修复 32 位 ARM 上拆卸时的崩溃 +- core: 添加 frida-inject,这是一种将 agent 注入正在运行的进程的工具,具有与 frida-gadget 类似的语义 +- core: (Linux) 防止 libdl 卸载以解决 TLS 析构函数错误 +- core: (Linux) 修复快速取消注入时的竞争条件 + +6.2.3: + +- core: 修复 eval 代码的源映射处理,这表现为未处理的异常被吞下,例如在运行 frida-trace 时 +- core: 修复 Python 3.x 构建系统回归 +- frida-trace: 修复路径转义问题 +- frida-trace: 改进坏处理程序的错误处理 + +6.2.4: + +- frida-trace: 监视处理程序而不是轮询它们 + +6.2.5: + +- core: 添加对通过使用函数而不是回调对象调用 *Interceptor.attach()* 来 hook 任意指令的支持 +- core: 添加对分离由 *Interceptor.attach()* 添加的单个监听器的支持,甚至可以从它们的回调中同步分离 +- core: 添加 *Memory.scanSync()* +- core: 通过改进 *Interceptor* 以在 ARM 上保留 *r12* 又名 *IP* 来修复破坏 +- core: 向 JavaScript 运行时公开 *r8* 到 *r12* +- core: 修复在不支持未对齐字访问的架构上的崩溃 +- frida-repl: 通过使用 RPC 功能简化逻辑 +- node: 升级到预构建 3.x + +6.2.6: + +- core: 修复非越狱 iOS 系统上的回归 +- core: 修复 Duktape 运行时中的 Interceptor 回归 +- core: 修复已解析导入的模块名称 +- core: 添加用于指定要连接的主机的 API +- core: 改进 QNX 支持并修复构建回归 +- core: 修复 Mac 上的 frida-inject 构建系统 +- core: (Windows) 修复 USB 设备位置检索失败时的崩溃 +- frida-server: 允许覆盖默认监听地址 +- frida-node: 向 DeviceManager 添加 *addRemoteDevice()* 和 *removeRemoteDevice()* +- frida-python: 添加 -H 开关用于指定要连接的主机 +- frida-python: 向 DeviceManager 添加 *add_remote_device()* 和 *remove_remote_device()* +- frida-python: 修复与 Duktape 运行时的兼容性问题 +- frida-python: 规范化请求的 RPC 方法名称 + +享受吧! diff --git a/_i18n/cn/_posts/2016-02-24-frida-7-0-released.markdown b/_i18n/cn/_posts/2016-02-24-frida-7-0-released.markdown new file mode 100644 index 00000000..133965a3 --- /dev/null +++ b/_i18n/cn/_posts/2016-02-24-frida-7-0-released.markdown @@ -0,0 +1,85 @@ +--- +layout: news_item +title: 'Frida 7.0 发布' +date: 2016-02-24 04:00:00 +0100 +author: oleavr +version: 7.0 +categories: [release] +--- + +距离我们上一次主要版本发布已经有一段时间了。这次我们解决了长期存在的问题,即 64 位整数被表示为 JavaScript Number 值。这意味着超过 53 位的值是有问题的,因为底层表示是 double。 + +*Memory*、*NativeFunction* 和 *NativeCallback* API 中的 64 位类型现在由新引入的 [Int64](/docs/javascript-api/#int64) 和 [UInt64](/docs/javascript-api/#uint64) 类型正确表示,它们的 API 几乎与 [NativePointer](/docs/javascript-api/#nativepointer) 相同。 + +现在让我们祈祷 int64/uint64 [进入 ES7](https://twitter.com/BrendanEich/status/526826278377099264)。 + +最后,这是更改的摘要: + +7.0.0: + +- core: 重做 64 位整数的处理 +- core: 提高构造函数的严格性 +- core: 改进 QNX 支持 +- frida-repl: 更新徽标 + +7.0.1: + +- core: 修复 32 位架构上的 Int64/UInt64 字段容量 + +7.0.2: + +- core: 允许将 Int64 和 UInt64 原样传递给所有相关 API +- core: 修复 ObjC 实例上 $protocols 的处理 + +7.0.3: + +- core: 修复监听器在调用中途被销毁的竞争条件 +- core: 修复嵌套本机异常范围的处理 +- core: 改进 QNX 支持 +- frida-repl: 调整启动消息 + +7.0.4: + +- core: 大幅提高 32 位 ARM 上的函数 hook 成功率 +- core: 提高 64 位 ARM 上的函数 hook 成功率 +- core: 修复 Interceptor 在 32 位 ARM 上公开的 *sp* 值 + +7.0.5: + +- core: 在启动 iOS 应用程序时等待 *Device#resume()* 时旋转主 CFRunLoop,允许从主线程应用线程敏感的早期插桩 + +7.0.6: + +- core: 修复 32 位 ARM 上半字对齐函数的 hook +- core: 修复 Linux 上的线程枚举 +- core: 向 Script 运行时添加简单的 *hexdump()* API +- core: 使 Duktape 运行时的 CpuContext 可序列化为 JSON + +7.0.7: + +- core: 允许将 *NativePointer* 传递给 *hexdump()* + +7.0.8: + +- core: 修复 `retval.replace()` 中包装对象的处理 +- core: 修复指定大小时 Memory.readUtf8String() 的行为 +- core: 添加对 iOS 9.1 JB 上新的 *task_for_pid(0)* 方法的支持 +- core: 不使用 *cbnz*,它在某些处理器上的 ARM 模式下不可用 +- core: 为 QNX 实现 *enumerate_threads()* 和 *modify_thread()* + +7.0.9: + +- core: 修复在 iOS 上使用 *ios-deploy* 和其他我们在 *CoreFoundation* 之前加载的环境运行时 FridaGadget.dylib 中的早期崩溃 +- core: 在 Darwin 上 *frida-helper* 的主线程中运行 *CFRunLoop*,允许系统会话脚本使用更多 Apple API +- core: 添加用于处理 GIO 流的流 API,目前仅通过 UnixInputStream 和 UnixOutputStream (UNIX) 以及 Win32InputStream 和 Win32OutputStream (Windows) 公开 + +7.0.10: + +- core: 修复 I/O 操作挂起时脚本卸载时的死锁 + +7.0.11: + +- core: 当 FridaGadget.dylib 阻塞等待 *Device#resume()* 时旋转主 CFRunLoop,允许从主线程应用线程敏感的早期插桩 +- java: 修复方法类型健全性检查 + +享受吧! diff --git a/_i18n/cn/_posts/2016-04-04-frida-7-1-released.markdown b/_i18n/cn/_posts/2016-04-04-frida-7-1-released.markdown new file mode 100644 index 00000000..222725e5 --- /dev/null +++ b/_i18n/cn/_posts/2016-04-04-frida-7-1-released.markdown @@ -0,0 +1,107 @@ +--- +layout: news_item +title: 'Frida 7.1 发布' +date: 2016-04-04 02:00:00 +0100 +author: oleavr +version: 7.1 +categories: [release] +--- + +如果您曾经使用 Frida 启动使用 stdio 的程序,您可能会对生成的进程的 stdio 状态如何相当未定义并且让您几乎无法控制感到沮丧。从这个版本开始,我们已经开始解决这个问题,程序现在总是通过重定向 *stdin*、*stdout* 和 *stderr* 来启动,您甚至可以将自己的数据输入到 *stdin* 并获取写入 *stdout* 和 *stderr* 的数据。Frida 的 CLI 工具免费获得此功能,因为这在 *ConsoleApplication* 基类中已 [wired up](https://github.com/frida/frida-python/blob/4afd9debd489e3920b85cb6c542de10aabb0dcce/src/frida/application.py#L212)。如果您不使用 *ConsoleApplication* 或者您使用不同的语言绑定,只需连接到 *Device* 对象的 *output* 信号,每次发出此信号时,您的处理程序都会获得三个参数:*pid*、*fd* 和 *data*,按此顺序。点击同一类上的 *input()* 方法以写入 *stdin*。这就是全部内容。 + +既然我们已经跨平台规范化了 stdio 行为,我们稍后将能够添加 API 来禁用 stdio 重定向。 + +除此之外和许多错误修复,我们还极大地改进了在 Darwin(Mac 和 iOS)上启动普通程序的支持,其中 *spawn()* 现在在两者上都快如闪电,并且不再弄乱 iOS 上的代码签名状态。 + +对于那些对 Mac 和 iOS 应用程序进行高级检测的人来说,现在还有全新的 API 用于在运行时动态创建自己的 Objective-C 协议。我们已经支持创建新类和代理对象,有了这个新 API,您可以做得更多。 + +最后,这是更改的摘要: + +7.1.0: + +- core: 添加 *Device.input()* API 用于写入生成进程的 *stdin* +- core: 添加 *Device.output* 信号用于传播来自生成进程的输出 +- core: 在 Windows、Darwin 和 Linux 后端实现新的 *spawn()* stdio 行为 +- core: 由于 4.x 中的非平凡回归,暂时降级到 Capstone 3.x +- node: 添加对新 stdio API 的支持 +- node: 添加错误路径缺少的返回 +- python: 添加对新 stdio API 的支持 + +7.1.1: + +- core: 修复 *spawn()* 中的间歇性崩溃 + +7.1.2: + +- core: 重做 Darwin 上的 *spawn()* 实现,现在更快更可靠 +- core: 添加对在 Darwin 上枚举和查找动态符号的支持 +- core: 修复 Darwin Mach-O 解析器中的页面大小计算 + +7.1.3: + +- core: 恢复临时 hack + +7.1.4: + +- python: 修复 EOF 时的 *ConsoleApplication* 崩溃 +- frida-trace: 退出前刷新排队的事件 + +7.1.5: + +- frida-repl: 改进 REPL 自动完成 +- objc: 添加 *ObjC.registerProtocol()* 用于动态协议创建 +- objc: 修复类名冲突的处理 +- objc: 允许代理被命名 + +7.1.6: + +- python: 修复 setup.py 下载回退 + +7.1.7: + +- python: 改进 setup.py 下载回退 + +7.1.8: + +- python: 修复 setup.py 本地回退并在主目录中查找 + +7.1.9: + +- core: 修复附加到同一 pid 的重叠请求的处理 +- core: (Darwin) 修复没有 *attach()* 的 *spawn()* +- core: (Darwin) 修复关闭请求重叠时的崩溃 +- frida-server: 始终回收相同的临时目录 + +7.1.10: + +- core: (Windows) 从 VS2013 升级到 VS2015 +- node: 添加 Node.js 6.x 的预构建 +- python: 修复 Python 2.x 上 unicode 命令行参数的处理 +- qml: 在 Mac 上使用 libc++ 而不是 libstdc++ + +7.1.11: + +- core: 当远程 Frida 不兼容时提供适当的错误消息 +- core: 忽略从系统会话分离的尝试 +- core: 防止 *create_script()* 和 *detach()* 重叠 +- core: 修复 *setTimeout()* 使延迟是可选的,默认为 0 +- core: (V8 runtime) 修复关闭的 *File* 对象被 GC 时的崩溃 +- core: (Darwin) 修复拆卸时的间歇性崩溃 +- core: (QNX) 修复 *gum_module_find_export_by_name()* 的实现 +- core: (QNX) 实现临时 TLS 存储 +- frida-repl: 监视加载的脚本并在更改时自动重新加载 +- node: 处理日志消息时考虑 *level*,以便 *console.warn()* 和 *console.error()* 转到 *stderr* 而不是 *stdout* +- node: 不让会话保持运行时活动 + +7.1.12: + +- core: 修复 *Memory.readByteArray()* 对于 size = 0 的返回值 + +7.1.13: + +- core: (Linux/Android) 修复具有首选基址的库的导出地址计算 +- core: 修复 Android 6.0 上的 *Java API not available* 错误 +- java: 通过考虑操作系统版本和架构来改进 ART 支持 +- frida-repl: 添加 *--no-pause* 以不在启动时暂停生成的进程 + +享受吧! diff --git a/_i18n/cn/_posts/2016-06-02-frida-7-2-released.markdown b/_i18n/cn/_posts/2016-06-02-frida-7-2-released.markdown new file mode 100644 index 00000000..3dc47a28 --- /dev/null +++ b/_i18n/cn/_posts/2016-06-02-frida-7-2-released.markdown @@ -0,0 +1,234 @@ +--- +layout: news_item +title: 'Frida 7.2 发布' +date: 2016-06-02 23:00:00 +0200 +author: oleavr +version: 7.2 +categories: [release] +--- + +你们中的一些人可能知道 Frida 有两个 JavaScript 运行时,一个基于 [V8](https://developers.google.com/v8/),另一个基于 [Duktape](http://duktape.org/)。我们过去也有一个基于 [JavaScriptCore](https://trac.webkit.org/wiki/JavaScriptCore) 的运行时,但当我们的 Duktape 运行时证明在 V8 不适合的所有情况下(例如在微型嵌入式系统和禁止 RWX 页面的系统上)都更好时,它就退役了。 + +无论如何,非常整洁的是 Duktape 有一个用于编译为字节码的 API,允许您缓存编译后的代码并在需要检测新进程时节省宝贵的启动时间。从这个版本开始,我们现在有了全新的 API 用于将您的 JavaScript 编译为字节码,当然还有从中实例化脚本。我们的 V8 运行时目前还不支持此 API,但我们应该能够在下次 V8 升级后,通过使用最新版本中开始出现的 WebAssembly 基础设施在那里面实现它。 + +因此,事不宜迟,让我们通过 *Session.disable_jit()* 强制 Frida 偏向 Duktape,用 Duktape 运行时试用这个新 API。 + +从 Node.js: + +{% highlight js %} +const co = require('co'); +const frida = require('frida'); + +co(function* () { + const systemSession = yield frida.attach(0); + yield systemSession.disableJit(); + const bytecode = yield systemSession.compileScript(` + rpc.exports = { + listThreads() { + return Process.enumerateThreadsSync(); + } + }; + `); + + const session = yield frida.attach('Twitter'); + yield session.disableJit(); + const script = yield session.createScriptFromBytes(bytecode); + yield script.load(); + + const api = yield script.getExports(); + console.log('api.listThreads() =>', yield api.listThreads()); + + yield script.unload(); +}) +.catch(err => { + console.error(err); +}); +{% endhighlight %} + +从 Python: + +{% highlight python %} +import frida + +system_session = frida.attach(0) +system_session.disable_jit() +bytecode = system_session.compile_script(""" +rpc.exports = { + listThreads() { + return Process.enumerateThreadsSync(); + } +}; +""") + +session = frida.attach("Twitter") +session.disable_jit() +script = session.create_script_from_bytes(bytecode) +script.load() + +api = script.exports +print("api.list_threads() =>", api.list_threads()) +{% endhighlight %} + +请注意,[Duktape 文档中指定的](http://duktape.org/api.html#duk_load_function)相同警告适用于此处,因此请确保您尝试加载的代码格式正确且由相同版本的 Duktape 生成。当您升级到 Frida 的未来版本时,它可能会升级,但至少是架构中立的;即您可以在 64 位 x86 桌面上编译为字节码,并在 ARM 上的 32 位 iOS 应用程序中正常加载它。 + +这就是通过 API 进行的字节码编译,但您可能希望使用 [frida-compile](https://github.com/frida/frida-compile) CLI 工具来代替: + +{% highlight bash %} +$ npm install frida-compile +$ ./node_modules/.bin/frida-compile agent.js -o agent.bin -b +{% endhighlight %} + +在开发时,您还可以通过添加 *-w* 在监视模式下使用它,这使得它监视输入并在其中一个更改时执行快速增量构建。 + +无论您是否使用字节码 (*-b*),都强烈建议使用 frida-compile,因为它还附带了许多其他好处,让您: + +- 通过使用 *require()* 将脚本拆分为多个 .js 文件。 +- 利用 npm 中的数千个现有模块,包括一些 Frida 特定的模块。例如: + [frida-trace](https://github.com/nowsecure/frida-trace), + [frida-uikit](https://github.com/nowsecure/frida-uikit), + [frida-screenshot](https://github.com/nowsecure/frida-screenshot) 等。 +- 使用 ES6 语法并将代码编译为 ES5,以便与 Duktape 运行时兼容。 + +最后,让我们总结一下变化: + +7.2.0: + +- core: 添加对编译和加载字节码的支持 +- core: 在 RPC 错误回复中包含错误名称和堆栈跟踪 +- node: 添加对新字节码 API 的支持 +- node: 在可用时用 *name* 和 *stack* 增强 RPC 错误 +- node: 将示例移植到 ES6 +- python: 添加对新字节码 API 的支持 +- python: 更新到修订后的 RPC 协议 + +7.2.1: + +- objc: 添加对在最小 Objective-C 代理上解析方法的支持 + +7.2.2: + +- objc: 修复返回结构体和浮点值的方法的处理 + +7.2.3: + +- objc: 公开 Objective-C 方法的原始句柄 + +7.2.4: + +- core: 修复在 iOS 9 上容易重现的死锁 +- java: 提高 Java.perform() 的健壮性和非应用程序进程的处理 + +7.2.5: + +- objc: 修复在 x86-64 上返回寄存器中结构体的方法的处理 + +7.2.6: + +- core: 将 Gum 移植到 MIPS +- core: 避免在 Proxy 对象行为不端时吞下异常 +- objc: 添加对访问 Objective-C 实例变量的支持 + +7.2.7: + +- core: 将 .so 注入器移植到 MIPS +- core: 用更多分支和链接指令增强 MIPS 模糊回溯器 +- core: 修复 TTY 上的 *UnixInputStream* 和 *UnixOutputStream* 可轮询行为,修复脚本卸载时的挂起 +- core: 从 *hexdump()* 偏移量中删除"0x"前缀 + +7.2.8: + +- objc: 修复类型提示的解析 +- objc: 添加对包含类型提示的支持 +- objc: 使 ObjC.Block 的 *types* 字段公开 +- objc: 添加对正确声明 *void \** 的支持 +- core: (MIPS) 修复获取/设置堆栈参数时的堆栈偏移量 + +7.2.9: + +- core: 修复阻止在 V8 运行时中写入寄存器的错误 + +7.2.10: + +- core: 添加对附加到 iOS 模拟器进程的支持 +- core: 修复 7.2.4 中引入的 Android 类解析回归 + +7.2.11: + +- core: 始终通过 SpringBoard 杀死 iOS 应用程序 + +7.2.12: + +- objc: 在卸载和 GC 时注销 Objective-C 类 + +7.2.13: + +- core: 修复 iOS 9 上的应用程序终止逻辑 + +7.2.14: + +- core: 使 Duktape 运行时像 V8 运行时一样可抢占 +- core: 修复 V8 运行时中的一些锁定错误 + +7.2.15: + +- core: 在 Duktape 运行时中也实现 *Kernel* API +- core: 删除危险的 *Kernel.enumerateThreads()* API + +7.2.16: + +- core: 提高快速重新附加到同一进程时的健壮性 +- core: 修复分离时存在挂起调用时的死锁 +- core: 修复 32 位 ARM 上的 hook 回归 +- core: 修复 Linux 上 frida-gadget 中的 *dlsym()* 死锁 +- core: 修复 Windows 构建回归 +- core: 修复 iOS 7 回归 + +7.2.17: + +- core: 修复会话拆卸回归 + +7.2.18: + +- core: 修复 iOS 9 上长期存在的稳定性问题,即注入的引导代码未伪签名,导致进程最终失去其 *CS_VALID* 状态 +- core: 通过消除不必要的磁盘 I/O 加速 iOS 上的应用程序启动 +- core: 修复 iOS 上的临时目录清理 + +7.2.19: + +- core: 修复 Duktape 运行时中与抢占相关的生命周期问题 + +7.2.20: + +- core: 重做 V8 运行时以支持完全异步卸载 +- core: 重做 Duktape 运行时以支持完全异步卸载 +- core: 使 Duktape 运行时完全可重入 +- core: 添加 *Script.pin()* 和 *Script.unpin()* 用于在关键时刻延长脚本的生命周期,例如对于预期来自无法控制的外部 API 的回调 +- core: 修复 V8 和 Duktape 运行时中与计时器相关的泄漏 +- objc: 保持脚本存活直到 *ObjC.schedule()* 调度的回调已执行 +- objc: 向 ObjC 代理 API 添加 *dealloc* 事件 + +7.2.21: + +- core: 修复 *detach()* 时的挂起 + +7.2.22: + +- core: 修复脚本卸载时的挂起 +- core: 修复 *detach()* 期间突然连接丢失时的挂起 + +7.2.23: + +- core: 修复脚本卸载期间的两个低概率崩溃 + +7.2.24: + +- core: 修复 Duktape 运行时中的 use-after-free +- core: 修复 ModuleApiResolver 中的 use-after-free 错误 +- core: 改进设置异常处理程序时的卸载行为 + +7.2.25: + +- core: 修复 iOS 9.3.3 上的应用程序启动 +- frida-server: 修复当另一个客户端附加到同一进程时分离时的"挂起" + +享受吧! diff --git a/_i18n/cn/_posts/2016-08-15-frida-7-3-released.markdown b/_i18n/cn/_posts/2016-08-15-frida-7-3-released.markdown new file mode 100644 index 00000000..edb8e0c6 --- /dev/null +++ b/_i18n/cn/_posts/2016-08-15-frida-7-3-released.markdown @@ -0,0 +1,48 @@ +--- +layout: news_item +title: 'Frida 7.3 Released' +date: 2016-08-15 23:00:00 +0200 +author: oleavr +version: 7.3 +categories: [release] +--- + +终于到了发布时间,这次的重点是提高质量。因为距离我们上次升级第三方依赖项已经有一段时间了,而且我发现自己正在追踪 GLib 中一个已经在上游修复的内存泄漏,我想是时候升级我们的依赖项了。因此,在这个版本中,我很高兴地宣布我们现在打包了最新的 V8、GLib、Vala 编译器等。我们也非常注意消除资源泄漏,因此您可以附加到长时间运行的进程,而不必担心内存分配或操作系统句柄堆积。 + +最后,让我们总结一下变化: + +7.3.0: + +- core: 升级到最新的 V8、GLib、Vala、Android NDK 等 +- core: 堵塞资源泄漏 +- core: 修复 Linux/x86-32 上的线程枚举 +- core: (arm64) 通过添加对重定位具有 FP/SIMD 目标寄存器的 LDRPC 的支持来改进函数 hook + +7.3.1: + +- core: 像以前一样使用 PIE 构建 Android 二进制文件 + +7.3.2: + +- core: 添加 *Script.setGlobalAccessHandler()* 用于处理访问未声明全局变量的尝试,这对于构建 REPL 很有用 + +7.3.3: + +- objc: 当期望对象时将 *Number* 转换为 *NSNumber* +- objc: 添加对自动转换为对象数组的支持,在调用例如 *+[NSArray arrayWithObjects:count:]* 时很有用 + +7.3.4: + +- core: 改进不稳定的访问器 API +- core: 修复 Duktape 全局访问器逻辑,使其仅应用于读取 + +7.3.5: + +- core: 改进 *hexdump()* 以支持任何符合 *NativePointer* 的对象 +- objc: 修复 *L* 类型的处理 + +7.3.6: + +- core: 修复 devkit 头文件自动生成逻辑中的回归 + +享受吧! diff --git a/_i18n/cn/_posts/2016-10-04-frida-8-0-released.markdown b/_i18n/cn/_posts/2016-10-04-frida-8-0-released.markdown new file mode 100644 index 00000000..a483ca68 --- /dev/null +++ b/_i18n/cn/_posts/2016-10-04-frida-8-0-released.markdown @@ -0,0 +1,64 @@ +--- +layout: news_item +title: 'Frida 8.0 发布' +date: 2016-10-04 23:00:00 +0200 +author: oleavr +version: 8.0 +categories: [release] +--- + +是时候升级到下一个主要版本了。 + +首先是长期存在的问题,即附加到同一进程的多个 Frida 客户端被迫协调,以便在其他客户端仍在使用会话时,没有一个客户端会调用 *detach()*。 + +对于大多数 Frida 用户来说,这可能不是什么大问题。但是,如果一个正在运行的 *frida-server* 由多个客户端共享,我们也会遇到同样的问题。您可能在一个终端中运行 *frida-trace*,而在另一个终端中使用 REPL,两者都附加到同一个进程,然后您不会希望其中一个调用 *detach()* 导致另一个被踢出。 + +你们中的一些人可能已经尝试过这个,并观察到它按预期工作,但这归功于 frida-server 中一些疯狂的逻辑,它会跟踪有多少客户端对同一个进程感兴趣,所以如果其他客户端仍然订阅了同一个会话,它可以忽略 *detach()* 调用。如果某个客户端突然断开连接,它还有一些逻辑来清理该客户端的资源,例如脚本。 + +从 8.0 开始,我们将感知会话移到了 agent 中,并保持面向客户端的 API 不变,但更改了一个小细节。每次调用 *attach()* 现在都会获得自己的 Session,并且注入的 agent 知道它。这意味着您可以随时调用 *detach()*,并且只有在您的会话中创建的脚本才会被销毁。此外,如果您的会话是最后一个活动的,Frida 将从目标进程中卸载其 agent。 + +那是此版本的重大变化,但我们并没有止步于此。 + +Frida 脚本的一个重要特性是您可以与它们交换消息。脚本可以调用 *send(message[, data])* 发送 JSON 可序列化的 *message*,并可选地在旁边发送 *data* 的二进制 blob。后者是为了让您不必花费 CPU 周期将二进制数据转换为包含在 *message* 中的文本。 + +也可以在另一个方向进行通信,当您从应用程序向其 *post_message()* 时,脚本将调用 *recv(callback)* 以获得 *callback*。这允许您向脚本发布 JSON 可序列化的 *message*,但不支持在旁边发送 *data* 的二进制 blob。 + +为了解决这个缺点,我们将 *post_message()* 重命名为 *post()*,并给它一个可选的第二个参数,允许您在旁边发送 *data* 的二进制 blob。 + +我们还通过从纯 C 数组迁移到 [GBytes](https://developer.gnome.org/glib/stable/glib-Byte-Arrays.html#GBytes) 改进了 C API,这意味着我们能够最大限度地减少数据流经我们的 API 时复制数据的次数。 + +最后,让我们总结一下变化: + +8.0.0: + +- core: 添加对多个并行会话的支持 +- core: 将 Script 的 *post_message()* 重命名为 *post()* 并添加对向脚本传递带外二进制数据的支持 +- core: 用 *GBytes* 替换 C 数组以提高性能 +- core: 修复 libgee 中 use-after-free 引起的堆损坏 +- core: 修复多个崩溃 +- core: 修复 macOS Sierra 上的导出枚举崩溃 +- core: 添加对在 Valgrind 上运行的基本支持 +- core: 将 macOS 要求提升到 10.9,以便我们可以依赖 libc++ +- node: 更新到新的 8.x API +- python: 更新到新的 8.x API +- swift: 更新到新的 8.x API +- swift: 升级到 Swift 3 +- qml: 更新到新的 8.x API +- clr: 更新到新的 8.x API +- clr: 堵塞泄漏 + +8.0.1: + +- node: 修复 *Script#post()* + +8.0.2: + +- core: 修复从我们的 JS 线程调用 *recv().wait()* 时的死锁 + +8.0.3: + +- core: 将 Interceptor 基本开销减少高达 65% +- core: 在我们的 V8 运行时中最大限度地减少 Interceptor GC 搅动,使用与我们的 Duktape 运行时相同的回收和写时复制技巧 +- core: 加速 macOS 和 iOS 上的 *gum_process_get_current_thread_id()* + +享受吧! diff --git a/_i18n/cn/_posts/2016-10-25-frida-8-1-released.markdown b/_i18n/cn/_posts/2016-10-25-frida-8-1-released.markdown new file mode 100644 index 00000000..8a6f50cb --- /dev/null +++ b/_i18n/cn/_posts/2016-10-25-frida-8-1-released.markdown @@ -0,0 +1,251 @@ +--- +layout: news_item +title: 'Frida 8.1 发布' +date: 2016-10-25 20:00:00 +0200 +author: oleavr +version: 8.1 +categories: [release] +--- + +是时候发布一个版本了,这次我们为那些构建基于 Frida 的工具的人带来了一些重要的新东西,外加一些额外的好东西。让我们从第一部分开始。 + +毫无疑问,Frida 的 [JavaScript API][] 相当低级,仅旨在提供不属于特定用例的低级构建块。例如,如果您的用例涉及在 iOS 上抓取屏幕截图,这不是人们期望在 Frida 本身中找到的功能。 + +您可能会想,具有共同功能的不同工具应该如何彼此共享 agent 代码,幸运的是答案不是"复制粘贴"。我们有一个不断增长的 Frida 特定库的 [ecosystem][],如 [frida-screenshot][]、[frida-uikit][]、[frida-trace][] 等。 + +也许你们中的一些人会对用于检测用 Java、.NET、Python、Ruby 或 Perl 编写的后端软件的 API 感兴趣,或者也许您想跨不同的操作系统和库跟踪加密 API,或者其他一些很酷的想法。那么我强烈建议您将模块发布到 npm,也许将您的模块命名为 *frida-$name* 以便于发现。 + +现在您可能会问"但是 Frida 不支持 *require()*,我首先如何将我的 agent 代码拆分为多个文件?"。我很高兴您问了!这就是一个名为 [frida-compile][] 的方便的小 CLI 工具进入画面的地方。 + +您给它一个 *.js* 文件作为输入,它将负责将其依赖的任何其他文件捆绑到一个文件中。但与使用 *cat* 的自制连接解决方案不同,最终结果还会获得嵌入式源映射,这意味着堆栈跟踪中的文件名和行号是有意义的。模块也被分成单独的闭包,因此变量被包含并且永远不会冲突。您还可以使用最新的 JavaScript 语法,如 [arrow functions][]、[destructuring][] 和 [generator functions][],因为它会为您将代码编译为 ES5 语法。这意味着您的代码也可以在我们基于 Duktape 的运行时上运行,如果您在受限 iOS 设备上或运行 iOS >= 9 的越狱 iOS 设备上使用 Frida,则必须使用该运行时。 + +为了在开发时为您提供较短的反馈循环,frida-compile 还通过 *-w* 提供监视模式,因此您在开发 agent 时可以获得即时增量构建。 + +无论如何,理论够了。让我们看看如何使用 npm 中的现成 Web 应用程序框架,并将其注入任何进程。 + +首先,确保您安装了最新版本的 Node.js。接下来,创建一个空目录并将以下内容粘贴到名为"package.json"的文件中: + +{% highlight json %} +{ + "name": "hello-frida", + "version": "1.0.0", + "scripts": { + "prepublish": "npm run build", + "build": "frida-compile agent -o _agent.js", + "watch": "frida-compile agent -o _agent.js -w" + }, + "devDependencies": { + "express": "^4.14.0", + "frida-compile": "^2.0.6" + } +} +{% endhighlight %} + +然后在 agent.js 中,粘贴以下代码: + +{% highlight js %} +const express = require('express'); + +const app = express(); + +app + .get('/ranges', (req, res) => { + res.json(Process.enumerateRangesSync({ + protection: '---', + coalesce: true + })); + }) + .get('/modules', (req, res) => { + res.json(Process.enumerateModulesSync()); + }) + .get('/modules/:name', (req, res) => { + try { + res.json(Process.getModuleByName(req.params.name)); + } catch (e) { + res.status(404).send(e.message); + } + }) + .get('/modules/:name/exports', (req, res) => { + res.json(Module.enumerateExportsSync(req.params.name)); + }) + .get('/modules/:name/imports', (req, res) => { + res.json(Module.enumerateImportsSync(req.params.name)); + }) + .get('/objc/classes', (req, res) => { + if (ObjC.available) { + res.json(Object.keys(ObjC.classes)); + } else { + res.status(404).send('Objective-C runtime not available in this process'); + } + }) + .get('/threads', (req, res) => { + res.json(Process.enumerateThreadsSync()); + }); + +app.listen(1337); +{% endhighlight %} + +安装 frida-compile 并一步构建您的 agent: + +{% highlight bash %} +$ npm install +{% endhighlight %} + +然后将生成的 *_agent.js* 加载到正在运行的进程中: + +{% highlight bash %} +$ frida Spotify -l _agent.js +{% endhighlight %} + +您现在可以用 HTTP 请求访问它: + +{% highlight bash %} +$ curl http://127.0.0.1:1337/ranges +$ curl http://127.0.0.1:1337/modules +$ curl http://127.0.0.1:1337/modules/libSystem.B.dylib +$ curl http://127.0.0.1:1337/modules/libSystem.B.dylib/exports +$ curl http://127.0.0.1:1337/modules/libSystem.B.dylib/imports +$ curl http://127.0.0.1:1337/objc/classes +$ curl http://127.0.0.1:1337/threads +{% endhighlight %} + +太棒了。我们只用了不到 50 行代码就构建了一个具有 7 个不同端点的进程检查 REST API。这很酷的一点是,我们使用了为 Node.js 编写的现成 Web 应用程序框架。实际上,您可以使用任何依赖于 Node.js 内置 [net][] 和 [http][] 模块的现有模块。比如 [FTP server][]、[IRC client][] 或 [NSQ client][]。 + +所以在发布此版本之前,您可以使用前面提到的 Frida 特定模块。您还可以使用 npm 中的数千个其他模块,因为它们中的大多数不进行任何 I/O。现在有了这个版本,您还可以访问所有基于 *net* 和 *http* 的模块,这为 Frida 打开了更多很酷的用例。 + +如果您好奇这是如何实现的,我在 Frida 中添加了 *Socket.listen()* 和 *Socket.connect()*。这些是 [GIO][] 的 [SocketListener][] 和 [SocketClient][] 之上的最小包装器,它们已经是 Frida 技术栈的一部分,并被 Frida 用于其自身需求。所以这意味着我们的占用空间保持不变,没有添加依赖项。因为 frida-compile 在幕后使用 [browserify][],我们所要做的就是 [plug in] 我们自己的 *net* 和 *http* 内置函数。我只是移植了 Node.js 本身的原始 *net* 和 *http* 模块。 + +此版本还带来了一些其他好东西。*NativeFunction* 的一个长期限制是,调用需要您读取 *errno* (UNIX) 或调用 *GetLastError()* (Windows) 的系统 API 会很棘手。挑战在于,在您的 *NativeFunction* 调用和您尝试读出错误状态之间,Frida 自己的代码可能会破坏当前线程的错误状态。 + +进入 *SystemFunction*。它与 *NativeFunction* 完全一样,只是调用返回一个对象,该对象包装了返回值和紧随其后的错误状态。这是一个例子: + +{% highlight js %} +const open = new SystemFunction( + Module.getExportByName(null, 'open'), + 'int', + ['pointer', 'int']); +const O_RDONLY = 0; + +const path = Memory.allocUtf8String('/inexistent'); +const result = open(path, O_RDONLY); +console.log(JSON.stringify(result, null, 2)); +/* + * Which on Darwin typically results in the following output: + * + * { + * "value": -1, + * "errno": 2 + * } + * + * Where 2 is ENOENT. + */ +{% endhighlight %} + +此版本还允许您从传递给 *Interceptor.replace()* 的 *NativeCallback* 中读取和修改此系统错误值,如果您正在替换系统 API,这可能会派上用场。请注意,您已经可以使用 *Interceptor.attach()* 做到这一点,但在您不想调用原始函数的情况下,这不是一个选项。 + +另一个值得一提的重大变化是我们的 V8 运行时已被大量重构。代码现在更容易理解,添加新功能的工作量也少得多。不仅如此,我们的参数解析也由单个代码路径处理。这意味着我们所有的 API 对错误或缺失的参数都更有弹性,因此您会得到一个 JavaScript 异常,而不是让某些 API 做更少的检查并在您忘记参数的情况下愉快地使目标进程崩溃。 + +无论如何,这些是亮点。这是更改的完整摘要: + +8.1.0: + +- core: 添加 *Socket.listen()* 和 *Socket.connect()* +- core: 添加 *setImmediate()* 和 *clearImmediate()* +- core: 改进 *set{Timeout,Interval}()* 以支持传递参数 +- core: 修复 Interceptor 脏状态逻辑中与性能相关的错误 + +8.1.1: + +- core: 添加 *Script.nextTick()* + +8.1.2: + +- core: 教 *Socket.listen()* 和 *Socket.connect()* 关于 UNIX 套接字 +- core: 修复 *this.errno* / *this.lastError* 替换函数的处理 +- core: 添加 *SystemFunction* API 以在返回时获取 *errno* / *lastError* +- core: 修复使用 Stream API 进行 I/O 期间 *close()* 时的崩溃 +- core: 修复并合并 V8 运行时中的参数处理 + +8.1.3: + +- core: 暂时在 macOS 上禁用 Mapper,以确认这是否是报告的稳定性问题的根本原因 +- core: 向 NativeFunction 添加 *.call()* 和 *.apply()* +- objc: 修复不透明结构类型的解析 + +8.1.4: + +- core: 修复由无效使用 *v8::Eternal* 引起的 V8 运行时崩溃 +- frida-repl: 通过 *-e* 和 *-q* 添加批处理模式支持 + +8.1.5: + +- node: 仅为 6.0 (LTS) 和 7.0 生成预构建 + +8.1.6: + +- node: 除了 6.0 和 7.0 之外,还为 4.0 和 5.0 生成预构建 + +8.1.7: + +- objc: 修复代理某些代理时的无限递归 +- objc: 添加对代理非 NSObject 实例的支持 +- python: 修复作为成员函数的信号回调的删除 + +8.1.8: + +- core: 实现单指令 ARM 函数的 hook +- core: 堵塞某些架构上不可 hook 函数处理中的泄漏 +- core: 修复 *setImmediate()* 回调处理行为 +- core: 堵塞 *setTimeout()* 中的泄漏 +- core: 修复 Duktape 运行时中处理 *setTimeout(0)* 和 *setImmediate()* 的竞争条件 +- core: 修复 Duktape 运行时中处理 tick 回调时的崩溃 +- core: 修复 Duktape 运行时中的生命周期问题 +- core: 修复 Linux 上报告的模块大小 +- core: 修复在较新版本的 Android 上启动应用程序时的崩溃 +- core: 修复尝试启动未安装的 Android 应用程序的处理 +- core: 通过动态检测 Dalvik 和 ART 字段偏移量,提高与不同版本和风格的 Android 的兼容性 +- core: 修复较新版本 Android 上的卸载问题,该问题导致只有第一次 *attach()* 成功,后续尝试全部超时 +- core: 将 *ObjC* 和 *Java* 移动到发布到 npm 的自己的模块中,并使用 *frida-compile* 将它们烘焙到 Frida 的内置 JS 运行时中 +- java: 通过动态检测 ArtMethod 字段偏移量来改进 ART 支持 +- node: 更新依赖项 +- node: 修复未处理的 Promise 拒绝问题 + +8.1.9: + +- core: 修复由脚本卸载时的竞争条件引起的 use-after-free + +8.1.10: + +- core: 使 *ApiResolver* 和 *DebugSymbol* API 可抢占以避免死锁 + +8.1.11: + +- core: 在 macOS 和 iOS 上使用 Mach 异常处理程序,允许我们可靠地捕获已经拥有自己的 Mach 异常处理程序的应用程序中的异常 +- core: 修复 Duktape 运行时中 *InvocationContext* 写时复制逻辑中的泄漏,用于在 *onEnter* 和 *onLeave* 之间在 *this* 上存储数据时 + +8.1.12: + +- core: 修复 V8 运行时中的 *Interceptor* 参数替换问题,导致参数仅在第一次被替换 + +享受吧! + + +[JavaScript API]: https://frida.re/docs/javascript-api/ +[ecosystem]: https://www.npmjs.com/search?q=frida +[frida-screenshot]: https://www.npmjs.com/package/frida-screenshot +[frida-uikit]: https://www.npmjs.com/package/frida-uikit +[frida-trace]: https://www.npmjs.com/package/frida-trace +[frida-compile]: https://www.npmjs.com/package/frida-compile +[arrow functions]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions +[destructuring]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment +[generator functions]: https://github.com/tj/co +[net]: https://nodejs.org/api/net.html +[http]: https://nodejs.org/api/http.html +[FTP server]: https://github.com/frida/frida-net/tree/master/examples/ftp-server +[IRC client]: https://github.com/frida/frida-net/tree/master/examples/irc-client +[NSQ client]: https://github.com/frida/frida-net/tree/master/examples/nsq-client +[GIO]: https://developer.gnome.org/gio/stable/ +[SocketListener]: https://developer.gnome.org/gio/stable/GSocketListener.html +[SocketClient]: https://developer.gnome.org/gio/stable/GSocketClient.html +[browserify]: http://browserify.org/ +[plug in]: https://github.com/frida/frida-compile/blob/1eeb38d9453f812e7b404e83cb9b5d0e5dc26241/index.js#L22-L23 diff --git a/_i18n/cn/_posts/2017-01-09-frida-9-0-released.markdown b/_i18n/cn/_posts/2017-01-09-frida-9-0-released.markdown new file mode 100644 index 00000000..e387f081 --- /dev/null +++ b/_i18n/cn/_posts/2017-01-09-frida-9-0-released.markdown @@ -0,0 +1,116 @@ +--- +layout: news_item +title: 'Frida 9.0 发布' +date: 2017-01-09 01:00:00 +0200 +author: oleavr +version: 9.0 +categories: [release] +--- + +这次有一些重大变化。我们现在默认在所有平台上使用基于 [Duktape][] 的 JavaScript 运行时,iOS 应用程序启动不再搭载 Cydia Substrate,并且我们带来了一些巨大的性能改进。还有一些错误修复。 + +先说说 Duktape。Frida 的第一个 JS 运行时基于 [V8][],我对这个选择非常满意。然而很明显,有些用例并不适合它。 + +有些系统,例如 iOS,不允许 RWX 内存[1](#ios-rwx),而 V8 没有它就无法运行。另一个例子是资源受限的嵌入式系统,那里根本没有足够的内存。而且,正如用户不时报告的那样,有些进程决定将其线程配置为具有微小的堆栈。然而 V8 非常消耗堆栈,所以如果您 hook 任何这些线程调用的函数,它不一定能够进入 V8,您的 hook 似乎被忽略了[2](#v8-stack)。 + +另一个方面是,对于本机 ⇔ JS 转换,V8 比 Duktape 昂贵得多,所以如果您的 Frida agent 全是关于 API hook,并且您的 hook 非常小,那么使用 Duktape 实际上可能会更好。Duktape 的垃圾收集也更可预测,这对于 hook 时间敏感代码很有好处。 + +也就是说,如果您的 agent 大量使用 JavaScript,V8 会快得多。它还带有本机 ES6 支持,尽管这并不是什么大问题,因为非简单的 agent 应该使用 [frida-compile][],它将您的代码编译为 ES5。 + +所以 V8 运行时不会消失,它将仍然是一等公民。唯一改变的是我们默认选择 Duktape,这样您就可以保证在所有平台上获得相同的运行时,并且很有可能会工作。 + +但是,如果您的用例大量使用 JS,您所要做的就是在创建第一个脚本之前调用 *Session#enable_jit()*,就会使用 V8。对于我们的 CLI 工具,您可以传递 *--enable-jit* 来获得相同的效果。 + +那是 Duktape。那么关于应用程序启动和 Substrate 的故事是什么?好吧,到目前为止,我们的 iOS 应用程序启动一直搭载在 Substrate 上。这是一个务实的解决方案,为了避免进入互操作性场景,其中 Frida 和 Substrate 都会 hook launchd 和 xpcproxy 中的 *posix_spawn()*,并互相踩踏。 + +然而,修复这个问题一直在我长期的待办事项清单上,因为它在其他领域增加了许多复杂性。例如,带外回调机制,以便我们的 Substrate 插件可以在加载时与我们对话,必须管理临时文件等。除此之外,这意味着我们依赖于闭源第三方组件,即使它是仅 iOS 应用程序启动所需的软依赖项。但是,它仍然是 Frida 唯一间接需要对运行系统进行永久修改的部分,我们真的想避免这种情况。 + +让我们看看新的应用程序启动是如何工作的。想象一下,您在连接了越狱 iOS 设备的主机上运行此命令: + +{% highlight bash %} +$ frida-trace -U -f com.atebits.Tweetie2 -i open +{% endhighlight %} + +我们告诉它启动 Twitter 的 iOS 应用程序并跟踪名为 *open* 的函数。顺便说一句,如果您对细节感到好奇,frida-trace 是用 Python 编写的,只有不到 900 行 [code][],所以这可能是了解更多关于在 Frida 之上构建自己的工具的好方法。或者也许您想改进 frida-trace?更好! + +它做的第一部分是获取第一个 USB 设备并在那里启动 Twitter 应用程序。这归结为: + +{% highlight py %} +import frida + +device = frida.get_usb_device() +pid = device.spawn(["com.atebits.Tweetie2"]) +{% endhighlight %} + +现在幕后发生的事情是这样的: + +1. 我们将 [launchd.js][] agent 注入 launchd(如果尚未完成)。 +2. 调用 agent 的 RPC 导出的 [prepareForLaunch()][],给它我们要启动的应用程序的标识符。 +3. 调用 [SBSLaunchApplicationWithIdentifierAndLaunchOptions()][] 以便 SpringBoard 启动应用程序。 +4. 我们的 launchd.js agent 然后拦截 launchd 的 *__posix_spawn()* 并添加 [POSIX_SPAWN_START_SUSPENDED][],并 [signals back][] 标识符和 PID。这是 */usr/libexec/xpcproxy* 辅助程序,它将执行 exec() 风格的转换以成为应用程序。 +5. 然后我们将 [xpcproxy.js][] agent 注入其中,以便它可以 hook *__posix_spawn()* 并添加 *POSIX_SPAWN_START_SUSPENDED*,就像我们的 launchd agent 所做的那样。然而,这个也将有 *POSIX_SPAWN_SETEXEC*,这意味着它将用要启动的应用程序替换自己。 +6. 我们 *resume()* xpcproxy 进程并 [wait for the exec][] 发生并且进程被挂起。 + +此时,我们让 *device.spawn()* 返回刚刚启动的应用程序的 PID。应用程序的进程已创建,主线程在 dyld 的入口点挂起。frida-trace 然后想要附加到它,以便它可以加载 hook *open* 的 agent。所以它继续做类似这样的事情: + +{% highlight py %} +session = device.attach(pid) +script = session.create_script(""" +Interceptor.attach(Module.getExportByName(null, 'open'), { + onEnter() { + console.log('open()'); + } +}); +""") +script.load() +{% endhighlight %} + +现在它已经应用了检测,它将要求 Frida 恢复进程,以便主线程可以调用 *main()* 并享受一些乐趣: + +{% highlight py %} +device.resume(pid) +{% endhighlight %} + +请注意,我在这里跳过了一些细节,因为 *attach()* 操作实际上由于进程未初始化的程度而稍微复杂一些,但您可以[在这里][here]阅读更多相关信息。 + +最后,让我们谈谈占用空间和性能。首先,让我们检查当 Frida 安装在 iOS 设备上并处于完全运行状态时需要多少磁盘空间: + + + +那是 64 位版本,xz 压缩后只有 1.87 MB。32 位版本显然更小。这里有相当多的优化在起作用: + +- 我们过去常常将 frida-helper 二进制文件写出到临时文件并启动它。frida-helper 程序的肉现在静态链接到 frida-server 中,其 entitlements 也随之提升。只有当 Frida 用作未知进程中的插件时,即我们无法对 entitlements 和代码签名做出任何保证的地方,才需要此二进制文件。然而,在 frida-server 案例中,它能够保证满足所有此类约束。 +- 我们注入到要检测的进程中的库 *frida-agent.dylib* 不再写出到临时文件。我们使用自己的进程外动态链接器将其从 frida-server 的内存映射并直接映射到目标进程的地址空间。这些映射是写时复制的,这意味着它与旧的 *dlopen()* 方法一样内存高效。 +- iOS 二进制文件禁用了 V8,因为它实际上仅在内核打补丁以允许 RWX 页面的旧越狱上可用。(如果 V8 对您的用例很重要,您可以像这样构建它:`make server-ios FRIDA_DIET=no`) +- iOS 包已拆分为两个,"Frida"用于 64 位设备,"Frida for 32-bit devices"用于旧设备。 +- 摆脱 iOS 应用程序启动的 Substrate 依赖也意味着我们摆脱了 FridaLoader.dylib。但这只是一个非常小的改进。 + +好吧,这就是磁盘占用空间。内存使用情况如何? + + + +不错。性能如何?让我们来看看: + + + +请注意,这些测量包括通过 USB 从 macOS 主机与 iOS 设备通信所花费的时间。 + +享受吧! + + +1 除非进程具有 entitlement,尽管这仅限于一个区域。[↩](#ios-rwx-sup) + +2: 技术上可以通过在调用 V8 之前切换到每线程侧堆栈来解决这个问题。我们过去实际上已经部分实现了这一点。这可能是我们应该在长期内恢复的东西。[↩](#v8-stack-sup) + +[Duktape]: http://duktape.org/ +[V8]: https://developers.google.com/v8/ +[frida-compile]: https://github.com/frida/frida-compile +[launchd.js]: https://github.com/frida/frida-core/blob/12be27ab171c8bac9b4a60db5b3957f30f4be938/src/darwin/agent/launchd.js +[prepareForLaunch()]: https://github.com/frida/frida-core/blob/12be27ab171c8bac9b4a60db5b3957f30f4be938/src/darwin/agent/launchd.js#L18-L21 +[SBSLaunchApplicationWithIdentifierAndLaunchOptions()]: https://github.com/frida/frida-core/blob/12be27ab171c8bac9b4a60db5b3957f30f4be938/src/darwin/frida-helper-backend-glue.m#L560-L563 +[POSIX_SPAWN_START_SUSPENDED]: https://github.com/frida/frida-core/blob/12be27ab171c8bac9b4a60db5b3957f30f4be938/src/darwin/agent/launchd.js#L60 +[signals back]: https://github.com/frida/frida-core/blob/12be27ab171c8bac9b4a60db5b3957f30f4be938/src/darwin/agent/launchd.js#L85 +[xpcproxy.js]: https://github.com/frida/frida-core/blob/12be27ab171c8bac9b4a60db5b3957f30f4be938/src/darwin/agent/xpcproxy.js +[wait for the exec]: https://github.com/frida/frida-core/blob/12be27ab171c8bac9b4a60db5b3957f30f4be938/src/darwin/darwin-host-session.vala#L476-L478 +[code]: https://github.com/frida/frida-python/blob/9c876f457cdee4d3dab6c05c8ab8c4bd72ca42d1/src/frida/tracer.py +[here]: https://github.com/frida/frida-core/blob/12be27ab171c8bac9b4a60db5b3957f30f4be938/src/darwin/frida-helper-backend-glue.m#L835-L861 diff --git a/_i18n/cn/_posts/2017-05-02-frida-10-0-released.markdown b/_i18n/cn/_posts/2017-05-02-frida-10-0-released.markdown new file mode 100644 index 00000000..1dd8671c --- /dev/null +++ b/_i18n/cn/_posts/2017-05-02-frida-10-0-released.markdown @@ -0,0 +1,23 @@ +--- +layout: news_item +title: 'Frida 10.0 发布' +date: 2017-05-02 23:00:00 +0200 +author: oleavr +version: 10.0 +categories: [release] +--- + +这次我们要把它提升一个档次。我们为您带来稳定性改进和最先进的 JavaScript 支持。 + +先说说稳定性改进。我们修复了一个影响所有 Linux 用户的堆损坏。这个问题特别难追踪,但 [rr][] 挽救了局面。另一个问题是 Duktape 运行时卸载时的崩溃,影响所有操作系统。 + +依赖项也升级了,所以从 Frida 10.0.0 开始,您现在可以享受几天前发布的 V8 6.0.124。我们还将 Duktape 升级到了最新的 2.1.x。Duktape 升级导致字节码语义发生细微变化,这意味着我们不得不稍微破坏我们的 API。现在不是在加载时指定脚本名称,而是在将其编译为字节码时指定,因为此元数据现在包含在字节码中。这更有意义,所以这是一个受欢迎的变化。 + +除了 V8 和 Duktape,我们还使用了最新的 GLib、Vala 编译器等。这些升级还包括 JSON-GLib,它最近放弃了 autotools 而支持 [Meson][]。这是个好消息,因为我们也计划在未来迁移到 Meson,所以我们现在已经为实现这一目标做了必要的准备工作。 + +大概就是这样。这次升级不需要对现有代码进行任何更改 —— 除非您是少数使用字节码 API 的人之一。 + +享受吧! + +[rr]: http://rr-project.org/ +[Meson]: http://mesonbuild.com/ diff --git a/_i18n/cn/_posts/2017-08-15-frida-10-4-released.markdown b/_i18n/cn/_posts/2017-08-15-frida-10-4-released.markdown new file mode 100644 index 00000000..75581951 --- /dev/null +++ b/_i18n/cn/_posts/2017-08-15-frida-10-4-released.markdown @@ -0,0 +1,128 @@ +--- +layout: news_item +title: 'Frida 10.4 发布' +date: 2017-08-15 23:00:00 +0200 +author: oleavr +version: 10.4 +categories: [release] +--- + +Frida 提供了相当多的构建块,使得跨许多操作系统和架构进行可移植检测变得容易。一个一直缺乏的领域是非可移植用例。虽然我们要提供一些原语,如 *Memory.alloc(Process.pageSize)* 和 *Memory.patchCode()*,使得分配和修改内存中代码成为可能,但没有任何东西可以帮助您实际生成代码。或者将代码从一个内存位置复制到另一个内存位置。 + +考虑到 Frida 需要为其自身需求生成和转换相当多的机器代码,例如实现 *Interceptor* 和 *Stalker*,我们已经有 C API 来跨六种不同的指令集风格做这些事情也就不足为奇了。最初这些 API 非常简陋,我看不到将它们公开给 JavaScript 的太多价值,但在多年有趣的内部用例之后,它们已经发展到基本部分现在被很好地覆盖的地步。 + +因此,在 10.4 中,我们终于将所有这些 API 公开给 JavaScript。值得一提的是,这些新绑定是自动生成的,因此未来的添加将毫不费力。 + +让我们看看 x86 上的一个例子: + +{% highlight js %} +const getLivesLeft = Module.getExportByName('game-engine.so', + 'get_lives_left'); +const maxPatchSize = 64; // Do not write out of bounds, may be + // a temporary buffer! +Memory.patchCode(getLivesLeft, maxPatchSize, code => { + const cw = new X86Writer(code, { pc: getLivesLeft }); + cw.putMovRegU32('eax', 9999); + cw.putRet(); + cw.flush(); +}); +{% endhighlight %} + +这意味着我们将目标函数的开头简单地替换为: + +{% highlight nasm %} +mov eax, 9999 +ret +{% endhighlight %} + +即假设返回类型是 `int`,我们只是用 `return 9999;` 替换了函数体。 + +顺便说一句,您也可以使用 *Memory.protect()* 更改页面保护,然后继续在各处编写代码,但 *Memory.patchCode()* 非常方便,因为它还: + +- 确保刷新 CPU 缓存; +- 处理 iOS 上的代码签名边缘情况。 + +那是一个简单的例子。让我们尝试一些更疯狂的事情: + +{% highlight js %} +const multiply = new NativeCallback(function (a, b) { + return a * b; +}, 'int', ['int', 'int']); + +const impl = Memory.alloc(Process.pageSize); + +Memory.patchCode(impl, 64, code => { + const cw = new X86Writer(code, { pc: impl }); + + cw.putMovRegU32('eax', 42); + + const stackAlignOffset = Process.pointerSize; + cw.putSubRegImm('xsp', stackAlignOffset); + + cw.putCallAddressWithArguments(multiply, ['eax', 7]); + + cw.putAddRegImm('xsp', stackAlignOffset); + + cw.putJmpShortLabel('done'); + + cw.putMovRegU32('eax', 43); + + cw.putLabel('done'); + cw.putRet(); + + cw.flush(); +}); + +const f = new NativeFunction(impl, 'int', []); +console.log(f()); +{% endhighlight %} + +虽然这只是为了将 *42* 乘以 *7* 而跳了很多圈,但这个想法是为了说明调用函数(甚至回到 JavaScript)和跳转到标签实际上是多么容易。 + +最后,让我们看看如何将指令从一个内存位置复制到另一个内存位置。正确执行此操作通常比直接 *memcpy()* 复杂得多,因为某些指令与位置相关,需要根据其在内存中的新位置进行调整。让我们看看如何使用 Frida 的新重定位器 API 解决这个问题: + +{% highlight js %} +const impl = Memory.alloc(Process.pageSize); + +Memory.patchCode(impl, Process.pageSize, code => { + const cw = new X86Writer(code, { pc: impl }); + + const libcPuts = Module.getExportByName(null, 'puts'); + const rl = new X86Relocator(libcPuts, cw); + + while (rl.readOne() !== 0) { + console.log('Relocating: ' + rl.input.toString()); + rl.writeOne(); + } + + cw.flush(); +}); + +const puts = new NativeFunction(impl, 'int', ['pointer']); +puts(Memory.allocUtf8String('Hello!')); +{% endhighlight %} + +我们只用了几行代码就制作了自己的 *puts()* 副本。整洁! + +请注意,您还可以插入自己的指令,并使用 *skipOne()* 选择性地跳过指令,以防您想进行自定义检测。(这就是 Stalker 的工作原理。) + +无论如何,这就是它的要点。您可以在以下位置找到全新的 API 参考: + +- x86 + * [X86Writer](/docs/javascript-api/#x86writer) + * [X86Relocator](/docs/javascript-api/#x86relocator) +- arm + * [ArmWriter](/docs/javascript-api/#armwriter) + * [ArmRelocator](/docs/javascript-api/#armrelocator) + * [ThumbWriter](/docs/javascript-api/#thumbwriter) + * [ThumbRelocator](/docs/javascript-api/#thumbrelocator) +- arm64 + * [Arm64Writer](/docs/javascript-api/#arm64writer) + * [Arm64Relocator](/docs/javascript-api/#arm64relocator) +- mips + * [MipsWriter](/docs/javascript-api/#mipswriter) + * [MipsRelocator](/docs/javascript-api/#mipsrelocator) + +另请注意,*Process.arch* 对于确定使用哪个 writer/relocator 很方便。关于这一点,您可能想知道为什么 32 位和 64 位 x86 只有一个实现。原因是这些指令集非常接近,以至于拥有统一的实现是有意义的。这也使得编写稍微可移植的代码变得更容易,因为一些元寄存器名称是可用的。例如 `xax` 解析为 `eax` 与 `rax`,具体取决于您所在的进程类型。 + +享受吧! diff --git a/_i18n/cn/_posts/2017-08-25-frida-10-5-released.markdown b/_i18n/cn/_posts/2017-08-25-frida-10-5-released.markdown new file mode 100644 index 00000000..0c52f979 --- /dev/null +++ b/_i18n/cn/_posts/2017-08-25-frida-10-5-released.markdown @@ -0,0 +1,101 @@ +--- +layout: news_item +title: 'Frida 10.5 发布' +date: 2017-08-25 04:00:00 +0200 +author: oleavr +version: 10.5 +categories: [release] +--- + +在 [NowSecure][],我们一直在熬夜,喝了无数杯咖啡,天哪,这次我们为您带来了新闻。 + +继续上一版本低级好东西的精神,这次我们将向堆栈上层移动一层。我们将介绍一种使用新 CodeWriter API 的全新方式,使您能够将自己的指令编织到您选择的任何线程执行的机器代码中。我们谈论的是基于每个线程的惰性动态重新编译,并对编译过程进行精确控制。 + +但首先是一点背景。大多数使用 Frida 的人可能正在使用 [Interceptor][] API 来执行内联 hook,和/或通过 [ObjC][] 和 [Java][] API 进行方法混淆或替换。这个想法通常是修改您期望被调用的一些有趣的 API,并能够将执行转移到您自己的代码,以观察、增强或完全替换应用程序行为。 + +这种方法的一个缺点是修改了代码或数据,这种更改很容易被插桩到。但这没关系,因为在进行进程内插桩时,对宿主进程自己的代码不可见总是一场猫捉老鼠的游戏。 + +然而,当试图回答"在这个私有 API 背后,对于给定的输入实际上调用了哪些其他 API?"这个问题时,这些技术非常有限。或者,在进行逆向和模糊测试时,您可能想知道对于给定函数的两个已知输入,执行在哪里出现分歧。另一个例子是测量代码覆盖率。您可以使用 Interceptor 对指令级探针的支持,首先使用静态分析工具找到所有基本块,然后使用 Frida 在各处放置单次探针。 + +进入 Stalker。这不是一个新的 API,但它允许您做的事情相当有限。把它想象成一个每线程代码跟踪器,其中线程的原始机器代码被动态重新编译到新的内存位置,以便在原始指令之间编织插桩。 + +它惰性地进行这种重新编译,一次一个基本块。考虑到存在大量自修改代码,它会小心地缓存编译块,以防原始代码在事后发生变化。 + +Stalker 还不遗余力地重新编译代码,以便副作用是相同的。例如,如果原始指令是 CALL,它将确保推送到堆栈上的是原始下一条指令的地址,而不是下一条重新编译指令的地址。 + +无论如何,Stalker 历来就像是一个宠物项目中的宠物项目。很有趣,但多年来 Frida 的其他部分引起了我的大部分关注。不过也有一些很棒的例外。多年前,我和 [@karltk][] 坐下来决定让 Stalker 在敌对代码上运行良好时,进行了一些[有趣的结对编程会议][fun pair-programming sessions]。后来我把 [CryptoShark][] 放在一起,为了让人们对其潜力感到兴奋。一段时间过去了,突然 Stalker 收到了由 [Eloi Vanderbeken] 贡献的关键错误修复。今年早些时候,[Antonio Ken Iannillo][] 加入并将其移植到 arm64。然后,就在最近,[Erik Smit][] 出现并修复了一个关键错误,即我们会为 REP 前缀的 JCC 指令生成无效代码。耶! + +到目前为止,Stalker 的 API 确实非常有限。您可以告诉它跟踪一个线程,包括您所在的线程,这与内联 hook(即 Interceptor)结合使用非常有用。您唯一能做的两件事是: + +1. 告诉它您感兴趣的事件,例如 `call: true`,这将为每个 CALL 指令产生一个事件。这意味着 Stalker 将在每个此类指令之前添加一些日志代码,这将记录 CALL 发生的位置、目标及其堆栈深度。其他事件类型非常相似。 +2. 为特定目标添加您自己的调用探针,当对特定目标进行 CALL 时给您一个同步回调到 JavaScript。 + +我非常兴奋地宣布,我们刚刚介绍了您可以使用此 API 做的第三件事,这是一个游戏规则改变者。您现在可以自定义重新编译过程,而且非常简单: + +{% highlight js %} +const appModule = Process.enumerateModulesSync()[0]; +const appStart = appModule.base; +const appEnd = appStart.add(appModule.size); + +Process.enumerateThreadsSync().forEach(thread => { + console.log('Stalking ' + thread.id); + + Stalker.follow(thread.id, { + transform(iterator) { + const instruction = iterator.next(); + + const startAddress = instruction.address; + const isAppCode = startAddress.compare(appStart) >= 0 && + startAddress.compare(appEnd) === -1; + + do { + if (isAppCode && instruction.mnemonic === 'ret') { + iterator.putCmpRegI32('eax', 60); + iterator.putJccShortLabel('jb', 'nope', 'no-hint'); + + iterator.putCmpRegI32('eax', 90); + iterator.putJccShortLabel('ja', 'nope', 'no-hint'); + + iterator.putCallout(onMatch); + + iterator.putLabel('nope'); + } + + iterator.keep(); + } while ((instruction = iterator.next()) !== null); + } + }); +}); + +function onMatch (context) { + console.log('Match! pc=' + context.pc + + ' rax=' + context.rax.toInt32()); +} +{% endhighlight %} + +每当即将编译新的基本块时,都会同步调用 `transform` 回调。它为您提供了一个迭代器,然后您可以使用它来推动重新编译过程向前发展,一次一条指令。返回的 [Instruction][] 告诉您关于即将重新编译的指令您需要知道的内容。然后您调用 `keep()` 以允许 Stalker 像往常一样重新编译它。这意味着如果您想跳过某些指令,例如因为您已用自己的代码替换了它们,您可以省略此调用。迭代器还允许您插入自己的指令,因为它公开了当前架构的完整 CodeWriter API,例如 [X86Writer][]。 + +上面的示例确定了应用程序自己的代码在内存中的位置,并在属于应用程序本身的任何代码中的每个 RET 指令之前添加了一些额外的指令。此代码检查 `eax` 是否包含 60 到 90 之间的值,如果是,则调用 JavaScript 以让其实陈任意复杂的逻辑。此回调可以随意读取和修改寄存器。这种方法的好处是您可以将代码插入热代码路径并选择性地调用 JavaScript,从而可以轻松地在机器代码中进行非常快速的检查,但将更复杂的任务卸载到更高级别的语言。您还可以 `Memory.alloc()` 并让生成的代码直接写入那里,而根本不进入 JavaScript。 + +这就是 10.5 中的大新事物。特别感谢 [@asabil] 帮助塑造了这个新 API。 + +最后,唯一其他的重大变化是 Instruction API 现在公开了底层 [Capstone][] 指令的更多细节。Stalker 在 x86 和 arm64 上使用的内存也少得多,而且也更可靠。最后,[Process.setExceptionHandler()][] 现在是一个有文档的 API,连同我们的 [SQLite API][]。 + +享受吧! + +[NowSecure]: https://www.nowsecure.com/ +[Interceptor]: /docs/javascript-api/#interceptor +[ObjC]: /docs/javascript-api/#objc +[Java]: /docs/javascript-api/#java +[@asabil]: https://twitter.com/asabil +[@karltk]: https://twitter.com/karltk +[fun pair-programming sessions]: http://blog.kalleberg.org/post/833101026/live-x86-code-instrumentation-with-frida +[CryptoShark]: https://www.youtube.com/watch?v=hzDsxtcRavY +[Eloi Vanderbeken]: https://twitter.com/elvanderb +[Antonio Ken Iannillo]: https://twitter.com/AKIannillo +[Erik Smit]: https://github.com/erik-smit +[Instruction]: /docs/javascript-api/#instruction +[X86Writer]: /docs/javascript-api/#x86writer +[Capstone]: http://www.capstone-engine.org/ +[Process.setExceptionHandler()]: /docs/javascript-api/#process +[SQLite API]: https://frida.re/docs/javascript-api/#sqlitedatabase diff --git a/_i18n/cn/_posts/2017-09-29-frida-10-6-released.markdown b/_i18n/cn/_posts/2017-09-29-frida-10-6-released.markdown new file mode 100644 index 00000000..f119067c --- /dev/null +++ b/_i18n/cn/_posts/2017-09-29-frida-10-6-released.markdown @@ -0,0 +1,20 @@ +--- +layout: news_item +title: 'Frida 10.6 发布' +date: 2017-09-29 19:00:00 +0200 +author: oleavr +version: 10.6 +categories: [release] +--- + +是时候对 Frida 的 [Gadget](/docs/gadget/) 进行一些重大更新了。 + +在处理受限 iOS 和 Android 设备时,此组件非常有用,但现在也能够覆盖许多其他场景。 + +它的环境变量现在已经消失,取而代之的是可选的配置文件。因为某些应用程序可能会作为其"反调试"防御的一部分查找名称中带有"Frida"的加载库,所以我们现在允许您随意重命名 Gadget 的二进制文件。除此之外,它现在还支持三种不同的交互类型。 + +它可以像以前一样监听 TCP 端口,也可以从文件系统加载脚本并完全自主运行。后一部分过去非常有限,但现在非常灵活,因为您甚至可以告诉它从目录加载脚本,其中每个脚本可能有过滤器。这对于系统范围的篡改非常有用,并且应该允许更有趣的用例。 + +因此,事不宜迟,我敦促大家查看[此处](/docs/gadget/)提供的全新文档。 + +享受吧! diff --git a/_i18n/cn/_posts/2018-03-13-frida-10-7-released.markdown b/_i18n/cn/_posts/2018-03-13-frida-10-7-released.markdown new file mode 100644 index 00000000..3f0d8ea3 --- /dev/null +++ b/_i18n/cn/_posts/2018-03-13-frida-10-7-released.markdown @@ -0,0 +1,16 @@ +--- +layout: news_item +title: 'Frida 10.7 发布' +date: 2018-03-13 21:00:00 +0200 +author: oleavr +version: 10.7 +categories: [release] +--- + +iOS 用户欢呼吧:Frida 现在兼容 iOS 11 上最新的 [Electra][] 越狱!在撰写本文时,这意味着 1.0.4,但现在 Electra 似乎已经稳定下来,可以安全地假设我们也将兼容未来的更新。只要确保您运行的不是比 1.0.4 更旧的版本。 + +在其他新闻中,我们的 Android 支持在过去几个版本中有了显著改善,所以如果您稍微落后了:立即获取最新的 10.7.x。 + +享受吧! + +[Electra]: https://coolstar.org/electra/ diff --git a/_i18n/cn/_posts/2018-04-28-frida-10-8-released.markdown b/_i18n/cn/_posts/2018-04-28-frida-10-8-released.markdown new file mode 100644 index 00000000..cd972c5a --- /dev/null +++ b/_i18n/cn/_posts/2018-04-28-frida-10-8-released.markdown @@ -0,0 +1,173 @@ +--- +layout: news_item +title: 'Frida 10.8 发布' +date: 2018-04-28 06:07:45 +0200 +author: oleavr +version: 10.8 +categories: [release] +--- + +准备好迎接重大升级。这次我们解决了三个长期存在的限制 —— 全部在一个版本中。 + +### 限制 #1: fork() + +作为快速复习,这个老派的 UNIX API 克隆整个进程并将子进程的进程 ID 返回给父进程,将零返回给子进程。子进程获得父进程地址空间的副本,由于写时复制,通常成本非常小。 + +一旦涉及多个线程,处理这个问题就会变得棘手。只有调用 *fork()* 的线程在子进程中存活,所以如果任何其他线程恰好持有锁,这些锁仍将在子进程中持有,并且没有人会释放它们。 + +本质上,这意味着任何同时进行 fork 和多线程的应用程序都必须真正仔细设计。尽管大多数 fork 的应用程序都是单线程的,但 Frida 通过将 agent 注入其中有效地使它们成为多线程。另一个方面是文件描述符,它们是共享的,因此也必须仔细管理。 + +我非常兴奋地宣布,我们终于能够检测到 *fork()* 即将发生,暂时停止我们的线程,暂停我们的通信通道,并在之后重新启动。然后,您可以在让子进程继续运行之前,对其应用所需的插桩(如果有)。 + +### 限制 #2: execve(), posix_spawn(), CreateProcess() 和朋友们 + +或者用通俗的英语说:启动其他程序的程序,要么完全替换自己,例如 *execve()*,要么通过生成子进程,例如没有 *POSIX_SPAWN_SETEXEC* 的 *posix_spawn()*。 + +就像 *fork()* 发生后一样,您现在能够应用插桩并控制子进程何时开始运行其第一条指令。 + +### 限制 #3: 处理突然的进程终止 + +过去引起很多困惑的一个方面是如何将一些数据传递给 Frida 的 *send()* API,如果进程即将终止,所述数据可能实际上无法到达另一端。 + +到目前为止,规定的解决方案总是 hook *exit()*、*abort()* 等,这样您就可以做一个 *send()* 加上 *recv().wait()* 乒乓来刷新任何仍在传输中的数据。回想起来,这并不是一个好主意,因为它很难在多个平台上正确执行此操作。我们现在有一个更好的解决方案。 + +因此,让我们谈谈新的 API 和功能。 + +### 子进程门控 + +这就是我们解决前两个问题的方法。提供 *create_script()* 的 *Session* 对象现在也有 *enable_child_gating()* 和 *disable_child_gating()*。默认情况下,Frida 的行为将与以前一样,您必须通过调用 *enable_child_gating()* 来选择加入此新行为。 + +从那时起,任何子进程最终都会被挂起,您将负责使用其 PID 调用 *resume()*。*Device* 对象现在还提供了一个名为 *delivered* 的信号,您应该附加一个回调以接收任何出现的新子进程的通知。这就是您应该在调用 *resume()* 之前应用所需插桩(如果有)的地方。*Device* 对象还有一个名为 *enumerate_pending_children()* 的新方法,可用于获取挂起子进程的完整列表。进程将保持挂起并作为该列表的一部分,直到它们被您恢复,或最终被杀死。 + +这就是理论。让我们看一个实际的 [example][],使用 Frida 的 Python 绑定: + +{% highlight python %} +import frida +from frida.application import Reactor +import threading + +class Application(object): + def __init__(self): + self._stop_requested = threading.Event() + self._reactor = Reactor(run_until_return=lambda _: + self._stop_requested.wait()) + + self._device = frida.get_local_device() + self._sessions = set() + + self._device.on("delivered", lambda child: + self._reactor.schedule( + lambda: self._on_delivered(child))) + + def run(self): + self._reactor.schedule(lambda: self._start()) + self._reactor.run() + + def _start(self): + argv = ["/bin/sh", "-c", "cat /etc/hosts"] + print("✔ spawn(argv={})".format(argv)) + pid = self._device.spawn(argv) + self._instrument(pid) + + def _stop_if_idle(self): + if len(self._sessions) == 0: + self._stop_requested.set() + + def _instrument(self, pid): + print("✔ attach(pid={})".format(pid)) + session = self._device.attach(pid) + session.on("detached", lambda reason: + self._reactor.schedule(lambda: + self._on_detached(pid, session, reason))) + print("✔ enable_child_gating()") + session.enable_child_gating() + print("✔ create_script()") + script = session.create_script(""" +Interceptor.attach(Module.getExportByName(null, 'open'), { + onEnter(args) { + send({ + type: 'open', + path: Memory.readUtf8String(args[0]) + }); + } +}); +""") + script.on("message", lambda message, data: + self._reactor.schedule( + lambda: self._on_message(pid, message))) + print("✔ load()") + script.load() + print("✔ resume(pid={})".format(pid)) + self._device.resume(pid) + self._sessions.add(session) + + def _on_delivered(self, child): + print("⚡ delivered: {}".format(child)) + self._instrument(child.pid) + + def _on_detached(self, pid, session, reason): + print("⚡ detached: pid={}, reason='{}'" + .format(pid, reason)) + self._sessions.remove(session) + self._reactor.schedule(self._stop_if_idle, delay=0.5) + + def _on_message(self, pid, message): + print("⚡ message: pid={}, payload={}" + .format(pid, message["payload"])) + + +app = Application() +app.run() +{% endhighlight %} + +行动: + +{% highlight bash %} +$ python3 example.py +✔ spawn(argv=['/bin/sh', '-c', 'cat /etc/hosts']) +✔ attach(pid=42401) +✔ enable_child_gating() +✔ create_script() +✔ load() +✔ resume(pid=42401) +⚡ message: pid=42401, +↪payload={'type': 'open', 'path': '/dev/tty'} +⚡ detached: pid=42401, reason='process-replaced' +⚡ delivered: Child(pid=42401, parent_pid=42401, +↪path="/bin/cat", argv=['cat', '/etc/hosts'], +↪envp=['SHELL=/bin/bash', 'TERM=xterm-256color', …], +↪origin=exec) +✔ attach(pid=42401) +✔ enable_child_gating() +✔ create_script() +✔ load() +✔ resume(pid=42401) +⚡ message: pid=42401, +↪payload={'type': 'open', 'path': '/etc/hosts'} +⚡ detached: pid=42401, reason='process-terminated' +$ +{% endhighlight %} + +### 退出前刷新 + +至于第三个限制,即处理突然的进程终止,Frida 现在将拦截最常见的进程终止 API,并为您处理任何挂起数据的刷新。 + +但是,对于通过缓冲数据并仅定期执行 *send()* 来优化吞吐量的高级 agent,现在有一种方法可以在进程终止或脚本卸载时运行您自己的代码。您所要做的就是定义一个名为 *dispose* 的 [RPC][] 导出。例如: + +{% highlight js %} +rpc.exports = { + dispose() { + send(bufferedData); + } +}; +{% endhighlight %} + +### 结束语 + +基于 Frida 中全新的 *fork()* 处理,还有一个完全重做的 Android 应用程序启动实现。*frida-loader-{32,64}.so* 辅助 agent 现在已经消失了,我们的幕后 Zygote 插桩现在利用全新的子进程门控来完成所有繁重的工作。这意味着您也可以根据自己的需要插桩 Zygote。只要记得 *enable_child_gating()* 并 *resume()* 任何您不关心的子进程。 + +所以这个版本大概就是这样。享受吧! + + +[example]: https://github.com/frida/frida-python/blob/c846da1191e50e017235f29580c737f5b8555d9a/examples/child_gating.py +[RPC]: /docs/javascript-api/#rpc diff --git a/_i18n/cn/_posts/2018-05-05-frida-11-0-released.markdown b/_i18n/cn/_posts/2018-05-05-frida-11-0-released.markdown new file mode 100644 index 00000000..d6a21b87 --- /dev/null +++ b/_i18n/cn/_posts/2018-05-05-frida-11-0-released.markdown @@ -0,0 +1,280 @@ +--- +layout: news_item +title: 'Frida 11.0 发布' +date: 2018-05-05 23:51:56 +0200 +author: oleavr +version: 11.0 +categories: [release] +--- + +是时候彻底检修 *spawn()* API 并修复 spawn 和子进程门控 API 中的一些粗糙边缘了。 + +### spawn() + +假设您正在使用 Frida 的 Python 绑定,您目前会这样做: + +{% highlight python %} +pid = device.spawn(["/bin/cat", "/etc/passwd"]) +{% endhighlight %} + +或者启动一个 iOS 应用程序: + +{% highlight python %} +pid = device.spawn(["com.apple.mobilesafari"]) +{% endhighlight %} + +好吧,这就是您实际上可以用该 API 做的所有事情……除了 Python 和 Node.js 绑定没有公开的一件事。我们稍后会谈到这一点。在我们去那里之前,让我们看看 frida-core 中的底层 [API][],这些绑定将其公开给不同的语言: + +{% highlight vala %} +namespace Frida { + … + public class Device : GLib.Object { + … + public async uint spawn (string path, + string[] argv, string[] envp) + throws Frida.Error; + public uint spawn_sync (string path, + string[] argv, string[] envp) + throws Frida.Error; + } + … +} +{% endhighlight %} + +顺便说一句,那是 [Vala][] 代码,这是 frida-core 编写所用的语言。这是一种类似 C# 的语言,可以编译为 C,而且非常棒。但我离题了。第一个方法 *spawn()* 是异步的,允许调用线程在调用进行时做其他事情,而 *spawn_sync()* 阻塞直到操作完成。 + +这两个方法编译为以下三个 C 函数: + +{% highlight c %} +void frida_device_spawn (FridaDevice * self, + const gchar * path, + gchar ** argv, int argv_length, + gchar ** envp, int envp_length, + GAsyncReadyCallback callback, gpointer user_data); +guint frida_device_spawn_finish (FridaDevice * self, + GAsyncResult * result, GError ** error); +guint frida_device_spawn_sync (FridaDevice * self, + const gchar * path, + gchar ** argv, int argv_length, + gchar ** envp, int envp_length, + GError ** error); +{% endhighlight %} + +前两个构成 *spawn()*,您将调用第一个并给它一个回调,一旦该回调被调用,您将调用第二个 *spawn_finish()*,给它您的回调收到的 *GAsyncResult*。返回值是 PID,或者,如果失败,*error* 输出参数解释了出了什么问题。如果您好奇的话,这就是 [GIO][] 异步模式。 + +至于第三个,*spawn_sync()*,这就是 Frida 的 Python 绑定所使用的。我们的 Node.js 绑定实际上使用前两个,因为这些绑定是完全异步的。有一天,如果能通过集成 Python 3.5 中引入的 *async/await* 支持,将我们的 Python 绑定也迁移到完全异步,那就太好了。 + +无论如何,回到上面的例子,我提到有一些东西没有公开。如果您仔细观察 frida-core API,您会注意到有 *envp* 字符串数组。窥视绑定的底层,您会意识到我们确实没有公开这个,我们实际上是这样做的: + +{% highlight c %} + envp = g_get_environ (); + envp_length = g_strv_length (envp); +{% endhighlight %} + +所以这意味着我们传递了 Python 进程的任何环境。如果实际的 spawn 发生在完全不同的系统上,比如连接的 iOS 或 Android 设备上,那绝对不好。稍微减轻这个问题的是,在启动 iOS 和 Android 应用程序时 *envp* 被忽略,仅在启动常规程序时使用。 + +这个旧 API 的另一个问题是声明 *string[] envp* 意味着它不可为空,如果声明是 *string[]? envp* 就可以为空。这意味着无法区分想要在没有任何环境的情况下启动(直观上意味着"使用默认值")和空环境。 + +正当我准备修复 API 的这方面时,我意识到是时候修复它的其他几个长期存在的问题了,比如能够: + +- 在默认值之上提供一些额外的环境变量 +- 设置工作目录 +- 自定义 stdio 重定向 +- 传递特定于平台的选项 + +到目前为止,我们总是将 stdio 重定向到我们自己的管道,并通过 *Device* 上的 *output* 信号流式传输任何输出。还有 *Device.input()* 用于写入 *stdin*。这些 API 仍然相同,唯一的区别是我们不再默认进行这种重定向。不过,你们中的大多数人可能并不太在意这一点,因为我们没有为 iOS 和 Android 应用程序实现这种重定向。从这个版本开始,我们终于为 iOS 应用程序实现了它。 + +到现在为止,您可能想知道新 API 是什么样子的。让我们来看看: + +{% highlight vala %} +namespace Frida { + … + public class Device : GLib.Object { + … + public async uint spawn (string program, + Frida.SpawnOptions? options = null) + throws Frida.Error; + public uint spawn_sync (string program, + Frida.SpawnOptions? options = null) + throws Frida.Error; + } + … + public class SpawnOptions : GLib.Object { + public string[]? argv { get; set; } + public string[]? envp { get; set; } + public string[]? env { get; set; } + public string? cwd { get; set; } + public Frida.Stdio stdio { get; set; } + public GLib.VariantDict aux { get; } + + public SpawnOptions (); + } + … +} +{% endhighlight %} + +回到开头的 Python 示例,这些示例仍然无需任何更改即可工作。但是,代替: + +{% highlight python %} +device.spawn(["com.apple.mobilesafari"]) +{% endhighlight %} + +您现在还可以这样做: + +{% highlight python %} +device.spawn("com.apple.mobilesafari") +{% endhighlight %} + +因为第一个参数是要启动的 *program*。您仍然可以在这里传递一个 *argv*,它将用于设置 *argv* 选项,这意味着 *argv[0]* 将用于 *program* 参数。您也可以这样做: + +{% highlight python %} +device.spawn("/bin/busybox", argv=["/bin/cat", "/etc/passwd"]) +{% endhighlight %} + +如果您想替换整个环境而不是使用默认值: + +{% highlight python %} +device.spawn("/bin/ls", envp={ "CLICOLOR": "1" }) +{% endhighlight %} + +虽然在大多数情况下,您可能只想添加/覆盖几个环境变量,现在这也是可能的: + +{% highlight python %} +device.spawn("/bin/ls", env={ "CLICOLOR": "1" }) +{% endhighlight %} + +您可能还想使用不同的工作目录: + +{% highlight python %} +device.spawn("/bin/ls", cwd="/etc") +{% endhighlight %} + +或者也许您想重定向 stdio: + +{% highlight python %} +device.spawn("/bin/ls", stdio="pipe") +{% endhighlight %} + +如前所述,*stdio* 默认值为 *inherit*。 + +我们现在已经涵盖了所有的 *SpawnOptions*,除了最后一个:*aux*。这是用于特定于平台的选项的字典。使用 Python 绑定设置此类选项非常简单:任何无法识别的关键字参数最终都会进入该字典。 + +例如,要启动 Safari 并告诉它打开特定的 URL: + +{% highlight python %} +device.spawn("com.apple.mobilesafari", url="https://frida.re") +{% endhighlight %} + +或者也许您想在禁用 ASLR 的情况下启动 i/macOS 程序: + +{% highlight python %} +device.spawn("/bin/ls", aslr="disable") +{% endhighlight %} + +另一个例子是用特定的 activity 启动 Android 应用程序: + +{% highlight python %} +spawn("com.android.settings", activity=".SecuritySettings") +{% endhighlight %} + +这实际上是我们目前支持的所有 aux 选项 —— 很棒的是我们可以在不需要更新绑定的情况下添加新的选项。 + +但在我们继续之前,让我们快速看看使用我们的 Node.js 绑定时这个新 API 是什么样子的: + +{% highlight js %} +const pid = await device.spawn('/bin/sh', { + argv: ['/bin/sh', '-c', 'ls /'], + env: { + 'BADGER': 'badger-badger-badger', + 'SNAKE': true, + 'MUSHROOM': 42, + }, + cwd: '/usr', + stdio: 'pipe', + aslr: 'auto' +}); +{% endhighlight %} + +如您所见,第二个参数是一个带有选项的对象,那些无法识别的选项最终会进入 aux 字典。 + +### 11.0.0 中的其余变化 + +让我们总结一下其余的变化,从 *Device* 类开始: + +- *enumerate_pending_spawns()* 现在是 *enumerate_pending_spawn()* 以在语法上正确。 +- *spawned* 信号已重命名为 *spawn-added*,现在还有 *spawn-removed*。 +- *delivered* 信号已重命名为 *child-added*,现在还有 *child-removed*。 + +最后的变化是 *Child* 类的 *path*、*argv* 和 *envp* 属性现在都可以为空。这是为了能够区分例如"未提供 *envp*"和"提供了空 *envp*"。 + +### 11.0.1 中的变化 + +- core: 修复 32 位 ARM 进程的 agent 线程的堆栈对齐 +- core: 修复脆弱的 SELinux 规则修补 + +### 11.0.2 中的变化 + +- core: 堵塞 i/macOS 上 spawn() 逻辑中的 Mach 端口泄漏(长期存在的问题) + +### 11.0.3 中的变化 + +- core: 修复 iPhone 8 和 X 上由于 arm64 上错误的 tls_base 计算导致的崩溃 + +### 11.0.4 中的变化 + +- python: 更新元数据并提升要求 + +### 11.0.5 中的变化 + +- core: 修复与 Electra 的 Tweak Injector 的兼容性问题 +- core: 修复 iOS 上 *enumerate_processes()* 中的进程名称截断 +- java: Java.registerClass() 现在支持重载 +- packaging: 每个新版本现在都附带 Fedora 和 Ubuntu 的包 + +### 11.0.6 中的变化 + +- python: 修复依赖规范,以便从 PyPI 安装时 REPL 再次工作 + +### 11.0.7 中的变化 + +- core: Windows 上更好的子进程门控 API 覆盖 +- python: 从 PyPI 安装时,Linux 的 2.7 二进制文件不再损坏 + +### 11.0.8 中的变化 + +- core: 修复导致某些平台上系统会话设置崩溃的竞争 +- python: 修复解释器关闭时的死锁 + +### 11.0.9 中的变化 + +- java: 修复阻止早期插桩期间 *Java.registerClass()* 的问题 + +### 11.0.10 中的变化 + +- core: 修复枚举具有空 DT_GNU_HASH 的 ELF 模块的导入/导出时的崩溃 + +### 11.0.11 中的变化 + +- java: 修复类型兼容性检查,这通常导致调用错误的重载,进而导致 VM abort() + +### 11.0.12 中的变化 + +- core: 修复与最新 macOS Mojave 和 iOS 12 beta 的兼容性问题 +- core: 改进系统会话(即 PID 0)中可用的 iOS *Kernel* API +- frida-trace: 修复跟踪器脚本 *send()* 任意数据时的崩溃 + +### 11.0.13 中的变化 + +- core: 修复从未 *load()* 的脚本的拆卸挂起 + +### EOF + +大概就是这样。如果您没有阅读上周发布的 Frida 10.8 版本,请务必[在这里][here]阅读。 + +享受吧! + + +[API]: https://gist.github.com/oleavr/e6af8791adbef8fbde06 +[Vala]: https://wiki.gnome.org/Projects/Vala +[GIO]: https://developer.gnome.org/gio/stable/ch02.html +[here]: /news/2018/04/28/frida-10-8-released/ diff --git a/_i18n/cn/_posts/2018-07-12-frida-12-0-released.markdown b/_i18n/cn/_posts/2018-07-12-frida-12-0-released.markdown new file mode 100644 index 00000000..e9fd9490 --- /dev/null +++ b/_i18n/cn/_posts/2018-07-12-frida-12-0-released.markdown @@ -0,0 +1,150 @@ +--- +layout: news_item +title: 'Frida 12.0 发布' +date: 2018-07-12 12:00:00 +0200 +author: oleavr +version: 12.0 +categories: [release] +--- + +正如你们中的一些人可能已经注意到的那样,可能有一本关于 Frida 的 [book][] 正在编写中。在 [NowSecure][] 的日常工作中,我花了大量时间作为 Frida API 的用户,因此我经常想起过去的设计决策,我后来后悔了。尽管我多年来确实解决了其中的大部分问题,但有些问题解决起来太痛苦了,我一直把它们放在后面。快进到今天,考虑到所有这些问题出版一本书的想法让我觉得是时候咬紧牙关了。 + +这就是为什么我很高兴地宣布 Frida 12。我们终于达到了 Frida 演变的一个点,我们的基础可以被认为足够稳定,可以写一本书了。 + +让我们看看这些变化。 + +### CLI 工具 + +过去引起一些混淆的一件事是我们的 Python 绑定还附带了一些 CLI 工具。Frida 是一个用于构建工具的工具包,尽管我们提供了一些示例工具,但是否要安装它们应该由您决定。 + +到目前为止,这意味着任何使用我们的 Python 绑定构建工具的人最终都会依赖于 *colorama*、*prompt-toolkit* 和 *pygments*,因为我们的 CLI 工具恰好依赖于这些。 + +好吧,现在这改变了。如果您执行: + +{% highlight sh %} +$ pip install frida +{% endhighlight %} + +您现在只会获得我们的 Python 绑定。仅此而已。这个包没有依赖项。 + +不过,CLI 工具对您可能仍然有用,因此要安装它们,请执行: + +{% highlight sh %} +$ pip install frida-tools +{% endhighlight %} + +### 绑定中的便利 API + +当时看起来是个好主意的事情是让我们的语言绑定在 [Session][] 对象上提供一些便利 API。想法是,只需要枚举加载的模块和可能几个内存范围,然后读取或写入内存的简单用例,不必加载自己的 agent。因此,我们的 Python 和 Node.js 绑定都在幕后为您执行此操作。 + +那时与 agent 通信有点繁琐,因为 [rpc][] API 不存在,但即便如此,这也是一个糟糕的设计决策。[JS APIs][] 很多,并非所有 API 都可以在不引入新复杂性层的情况下公开。另一个方面是,每个语言绑定都必须复制这样的便利 API,或者我们必须添加绑定可以公开的核心 API。两者都是糟糕的选择,并通过模糊界限引起混淆,最终使 Frida 新手感到困惑。诚然,它确实使一些非常简单的用例(如内存转储工具)变得更容易,但对于其他所有人来说,它只是增加了膨胀和混乱。 + +这些 API 现在终于从我们的 Python 和 Node.js 绑定中消失了。其他绑定不受影响,因为它们没有实现任何此类便利 API。 + +### Node.js 绑定 + +自从我们的 Node.js 绑定编写以来已经过去了几年,从那时起 Node.js 已经发展了很多。它现在支持 ES6 类、*async* / *await*、箭头函数、*Proxy* 对象等。 + +仅 *Proxy* 支持就意味着我们可以简化 [rpc][] 用例,例如: + +{% highlight js %} +const api = await script.getExports(); +const result = await api.add(2, 5); +{% endhighlight %} + +到只是: + +{% highlight js %} +const result = await script.exports.add(2, 5); +{% endhighlight %} + +你们中的一些人可能还更喜欢用 [TypeScript][] 编写应用程序,与老式 JavaScript 相比,这是一个很棒的生产力提升。您不仅可以获得类型检查,而且如果您使用像 [VS Code][] 这样的编辑器,您还可以获得类型感知重构和惊人的代码完成。 + +然而,为了使类型检查和编辑器功能真正发光,拥有项目依赖项的类型定义至关重要。如今这很少是一个问题,除了那些使用 Frida 的 Node.js 绑定的人。到目前为止,我们没有提供任何类型定义。这终于得到了解决。我决定用 TypeScript 重写它们,而不是用类型定义来增强我们的绑定。这意味着我们还利用了现代语言功能,如 ES6 类和 *async* / *await*。 + +我们本可以就此打住,但那些从 TypeScript 使用我们的 Node.js 绑定的人仍然会发现这有点令人沮丧: + +{% highlight js %} +script.events.listen('message', (message, data) => { +}); +{% endhighlight %} + +在这里,编译器对 *Script* 对象上存在哪些事件一无所知,以及此特定事件的回调签名应该是什么。我们终于解决了这个问题。API 现在看起来像这样: + +{% highlight js %} +script.message.connect((message, data) => { +}); +{% endhighlight %} + +瞧。您的编辑器甚至可以告诉您支持哪些事件,并为回调中的代码提供适当的类型检查。太棒了! + +### Interceptor + +过去引起一些混淆的是,从 *onEnter* 或 *onLeave* 访问 *this.context.pc* 会给您返回地址,而不是您放置 hook 的指令的地址。这终于得到了修复。此外,*this.context.sp* 现在指向 x86 上的返回地址,而不是第一个参数。*Stalker* 在使用调用探针时也是如此。 + +作为破坏我们的回溯器实现的这次重构的一部分,我还改进了我们在 Windows 上的默认回溯器。 + +### Tether? + +您可能想知道为什么 `frida.get_usb_device()` 会给您一个 *type* 为 *'tether'* 的 *Device*。现在终于是 *'usb'* 了,正如您所期望的那样。因此,我们的语言绑定终于与我们的 [core API][] 一致了。 + +### 12.0.1 中的变化 + +- core: 修复 32 位 x86 上的参数访问 +- core: 将 *Stalker* 更新到新的 *CpuContext* 语义 +- python: 将正确的 README 发布到 PyPI +- python: 修复 Windows 构建系统 + +### 12.0.2 中的变化 + +- core: 升级到 Capstone 的 *next* 分支 +- core: 修复 Windows 上的 DbgHelp 回溯器并更新到最新的 DbgHelp +- python: 修复长描述 +- java: 修复 *java.lang.Class.getMethod()* 的 hook —— 感谢 [0x3430D][]! + +### 12.0.3 中的变化 + +- core: 修复 Capstone 升级破坏的 iOS 构建系统 + +### 12.0.4 中的变化 + +- core: 修复早期插桩时的 i/macOS libc++ 初始化 —— 感谢 [mrmacete][]! +- core: 添加对带掩码的内存搜索的支持 —— 感谢 [mrmacete][]! +- core: 修复 *InvocationContext.get_return_address()* +- node: 修复从异步函数返回 RPC 对象时的崩溃 + +### 12.0.5 中的变化 + +- core: 修复由 Capstone 升级引起的 arm64 崩溃,其中测试和分支解码逻辑无法解码负偏移量 —— 感谢 [mrmacete][] 发现并修复这个问题! +- core: 修复 MIPS 回归和一个崩溃器 —— 感谢 [r0ck3tAKATrashPanda][]! + +### 12.0.6 中的变化 + +- python: 改进 *spawn()* 以在 Python 2.x 上支持 unicode aux 选项 +- java: 修复缺少缓存目录时的 *Java.registerClass()* +- java: 使临时文件命名可配置 + +### 12.0.7 中的变化 + +- core: 修复 iOS 11.3.1 到 11.4.1 上的早期插桩 —— 感谢 [mrmacete][]! + +### 12.0.8 中的变化 + +- core: 修复使用自定义进程名称启动 Android 应用程序 —— 感谢 [giantpune][]! +- java: 修复 Android 8.0 上的 *ClassLinker* 字段偏移量检测 + +享受吧! + + +[NowSecure]: https://www.nowsecure.com/ +[book]: https://twitter.com/fridadotre/status/950085837445836800 +[Session]: https://gist.github.com/oleavr/e6af8791adbef8fbde06#file-frida-core-1-0-vapi-L201-L226 +[rpc]: https://frida.re/docs/javascript-api/#rpc +[JS APIs]: https://frida.re/docs/javascript-api/ +[TypeScript]: https://www.typescriptlang.org/ +[VS Code]: https://code.visualstudio.com/ +[core API]: https://gist.github.com/oleavr/e6af8791adbef8fbde06 +[0x3430D]: https://github.com/0x3430D +[mrmacete]: https://github.com/mrmacete +[r0ck3tAKATrashPanda]: https://github.com/r0ck3tAKATrashPanda +[giantpune]: https://github.com/giantpune diff --git a/_i18n/cn/_posts/2018-08-25-frida-12-1-released.markdown b/_i18n/cn/_posts/2018-08-25-frida-12-1-released.markdown new file mode 100644 index 00000000..4ba96a98 --- /dev/null +++ b/_i18n/cn/_posts/2018-08-25-frida-12-1-released.markdown @@ -0,0 +1,47 @@ +--- +layout: news_item +title: 'Frida 12.1 发布' +date: 2018-08-25 23:00:00 +0200 +author: oleavr +version: 12.1 +categories: [release] +--- + +这次底层有大量变化。我们所有的依赖项都已升级到最新最好的版本。让我们看看亮点。 + +### V8 7.0 + +Frida 的 V8 依赖项以前是 6.2.2,现在已升级到 7.0.242。迁移到如此新的版本意味着 V8 调试器 API 已消失,并已被新的 Inspector API 取代,最新的 Node.js 也在使用它。关于它的一个非常棒的事情是它得到了 Google Chrome 的 Inspector 的原生支持。 + +要开始使用它,只需告诉 Frida 使用 V8,通过调用 *session.enable_jit()*,然后调用 *session.enable_debugger()*。 + +或者在使用 CLI 工具时: + +{% highlight sh %} +$ frida-trace --enable-jit --debug -f /bin/cat -i read +{% endhighlight %} + +然后在 Google Chrome 中,右键单击并选择"检查",然后单击 Inspector 左上角的绿色 Node.js 图标。就是这样,您现在正在调试您的 Frida 脚本。这意味着一个带有自动完成的漂亮控制台、暂停/继续、单步执行、断点、分析和堆快照。使它真正方便的是服务器在您的主机上监听,因此您可以在表示 USB 系留的 Android 设备上的进程的会话上调用 *enable_debugger()*,它都以相同的方式工作。 + +这是它的样子: + +![Console](/img/inspector-console.png "Console") +![Profiler](/img/inspector-profiler.png "Profiler") +![Heap Snapshot](/img/inspector-snapshot.png "Heap Snapshot") + +但是请注意,V8 目前未包含在我们的预构建 iOS 二进制文件中,但现在应该可以在 iOS 上再次运行它,因为它能够在没有 RWX 页面的情况下运行。不过,我们确实计划在幕后桥接 Duktape 的二进制调试器协议,以便调试也可以与 Duktape "正常工作",尽管可能功能集略有减少。 + +### Process.id + +知道您的 agent 正在其中执行的进程的 PID 通常非常有用,尤其是在 [preloaded mode][] 中。因此,与其要求您使用 *NativeFunction* 来调用例如 *getpid()*,现在这要简单得多,因为您可以使用全新的 `Process.id` 属性。 + +### 还有别的吗? + +没有其他功能,但有一些非常好的错误修复。感谢 [mrmacete][],我们现在能够在 iOS 11.x 上附加到 *com.apple.WebKit.\\** 进程。感谢 [viniciusmarangoni][],此版本还为 frida-java 打包了一些好东西,修复了一个 Android 8.0 回归并添加了正确检测 *system_server* 的能力。 + +享受吧! + + +[preloaded mode]: https://frida.re/docs/modes/#preloaded +[mrmacete]: https://github.com/mrmacete +[viniciusmarangoni]: https://github.com/viniciusmarangoni diff --git a/_i18n/cn/_posts/2018-09-11-frida-12-2-released.markdown b/_i18n/cn/_posts/2018-09-11-frida-12-2-released.markdown new file mode 100644 index 00000000..e9845e05 --- /dev/null +++ b/_i18n/cn/_posts/2018-09-11-frida-12-2-released.markdown @@ -0,0 +1,55 @@ +--- +layout: news_item +title: 'Frida 12.2 发布' +date: 2018-09-11 21:00:00 +0200 +author: mrmacete +version: 12.2 +categories: [release] +--- + +让我们谈谈 iOS 内核内省。Frida 获得对 iOS 内核内省的基本支持已经有一段时间了,但在过去几个月中,我们一直在改进它。今天的版本包括对我们的 Kernel API 的重大补充,以支持最新的 64 位内核。 + +## 内核基址 + +您可以通过读取 `Kernel.base` 属性来获取内核的基地址。拥有基址可以例如计算您从内核缓存的静态分析中已经知道的任何符号的滑动虚拟地址。 + +## 内核内存搜索 + +内存搜索 API 已移植到 Kernel,因此您可以像在用户空间中使用 `Memory.scan()`(或 `Memory.scanSync()`)一样使用 `Kernel.scan()`(或 `Kernel.scanSync()`)。这是一个强大的原语,结合最近的位掩码功能,允许您通过搜索 arm64 模式来创建自己的符号查找代码。 + +## KEXT 和内存范围 + +使用 `Kernel.enumerateModules()`(或 `Kernel.enumerateModulesSync()`)现在可以获取所有 KEXT 的名称和偏移量。 + +`Kernel.enumerateModuleRanges()`(或 `Kernel.enumerateModuleRangesSync()`)是枚举属于模块(按名称)的 Mach-O 节定义的所有内存范围的方法,按保护进行过滤。结果类似于在用户空间中调用 `Module.enumerateRanges()` 时可以获得的结果,但它还包括节名称。 + +## 最后说明 + +所有 Kernel API 都不依赖于 `NativePointer`,因为其大小取决于用户空间,而用户空间不一定与内核空间匹配。相反,所有地址都表示为 `UInt64` 对象。 + +所有这些,加上现有的用于读取、写入和分配内核内存的 JavaScript 接口,可以为构建您自己的内核分析或漏洞研究工具提供强大的起点。 + +请注意,这被认为是实验性的,以随机方式搞乱内核可能会严重损坏您的设备,所以要小心,祝您 hacking 愉快! + +## 故障排除 + +### 问题:Kernel.available 为 false + +如果满足以下两个条件,则 Kernel API 可用: + +- 您的设备已越狱 +- Frida 能够获得对内核任务的发送权限,无论是通过传统的 `task_for_pid (0)` 还是通过访问主机特殊端口 4(这是现代越狱正在做的) + +完成后者的推荐方法是附加到系统会话,即 PID 0,并在那里加载您的脚本。 + +### 问题:无法对我的 32 位内核做太多事情 + +是的,这在将来可能会改进,但 32 位 iOS 现在在优先级列表中相当靠后,但非常欢迎您贡献并发送 PR。 + +### 问题:我试图做 X,内核崩溃了 + +别担心,这很正常。您可以转到设备上的 `/private/var/mobile/Library/Logs/CrashReporter` 目录,或导航到设置 -> 隐私 -> 分析 -> 分析数据,找到您的崩溃日志并弄清楚您(或 Frida)做错了什么。记住:内核总是对的! + +### 问题:我使用 Frida Kernel API 不可恢复地损坏了我的设备 + +很遗憾听到这个消息,如果损坏是在硬件级别,并且您可以投入足够的时间和金钱,您可能可以通过遵循 [https://ifixit.com](https://ifixit.com) 上的教程自己修复它。 diff --git a/_i18n/cn/_posts/2019-05-15-frida-12-5-released.markdown b/_i18n/cn/_posts/2019-05-15-frida-12-5-released.markdown new file mode 100644 index 00000000..f72a1ab6 --- /dev/null +++ b/_i18n/cn/_posts/2019-05-15-frida-12-5-released.markdown @@ -0,0 +1,147 @@ +--- +layout: news_item +title: 'Frida 12.5 发布' +date: 2019-05-15 23:00:00 +0200 +author: oleavr +version: 12.5 +categories: [release] +--- + +这是一个内容丰富的版本。有很多事情要谈。 + +### V8 + +这次的主要故事是 [V8][]。但首先,一点背景。 + +Frida 默认使用 [Duktape][] JavaScript 运行时来提供对其检测核心 [Gum][] 的可脚本化访问。我们最初只使用 V8,但由于 V8 过去依赖于对 *RWX* 页面的操作系统支持 —— 即也可写的可执行内存页面 —— 我们最终添加了基于 Duktape 的辅助 JS 运行时。由于 V8 的这种操作系统约束,以及 Duktape 缺乏对最新 JS 语法和功能的支持,我决定将 Duktape 作为我们的默认运行时,这样为一个平台编写的脚本就不需要任何语法更改就可以在另一个平台上工作。Duktape 基本上是最小公分母。 + +快进到今天,V8 不再依赖于对 *RWX* 页面的操作系统支持。它实际上正在朝着默认在 *RW-* 和 *R-X* 之间切换的方向发展。这样做意味着它可以在现代 iOS 越狱上使用,如果进程被标记为正在调试,也可以在受限 iOS 上使用,因此它能够运行未签名的代码。但还有更多;V8 现在甚至可以运行 [JIT-less][],这意味着 Frida 可以在每个平台上使用 V8,用户不再需要 [frida-compile][] 他们的 agent 来使用最新的 JavaScript 语法和功能。不过,最后一点仅适用于简单的 agent,因为能够将非简单的 agent 拆分为多个源文件仍然是可取的。此外,frida-compile 使使用 [TypeScript][] 变得容易,这对于任何非简单的 Frida agent 都是强烈推荐的。 + +因此,考虑到所有这些,显然是时候将我们的 V8 升级到最新最好的版本了。从这个版本开始,我们正在运行 [7.6.48][],并且我们与 V8 的集成比以往任何时候都更深入。C++ 内存分配和页级分配现在都由 Frida 管理,因此我们能够从 *Process.enumerateRanges()* 等 API 中隐藏这些内存范围,并避免用属于 Frida 的分配污染应用程序自己的堆。这些细节听起来可能并不那么重要,但实际上对于在 Frida 之上实现内存转储工具至关重要。然而,不仅如此,我们对正在观察的进程的干扰也更少。这意味着它表现出与没有检测时运行时不同行为的风险更小。 + +### 运行时选择 + +您可能还记得 *session.enable_jit()* API。它最终被弃用了,因为您现在可以在脚本创建期间指定所需的运行时。例如使用我们的 Python 绑定: + +{% highlight python %} +script = session.create_script(source, runtime='duk') +{% endhighlight %} + +使用我们的 Node.js 绑定: + +{% highlight js %} +const script = await session.createScript(source, { + runtime: 'v8' +}); +{% endhighlight %} + +### Stalker + +此版本中的另一个重大变化是 [Stalker][] 在 arm64 上不再依赖于 *RWX* 页面,这要归功于 [John Coates][] 的出色贡献。这意味着 Stalker 终于在 iOS 上更容易访问了。 + +对于那些在 64 位 Windows 上使用 Stalker 并跟踪 32 位进程的人,它终于处理了较新版本 Windows 上的 WOW64 转换。这个令人费解的改进是由 [Florian Märkl][] 贡献的。 + +### Module.load() + +有时您可能想要加载自己的共享库,可能包含用 C/C++ 编写的 hook。在大多数平台上,您可以通过使用 [NativeFunction][] 调用 *dlopen()*(POSIX)或 *LoadLibrary()*(Windows)来实现这一点。然而,在较新版本的 Android 上,情况却大不相同,因为它们的 *dlopen()* 实现会查看调用者并根据它做出决定。其中一个决定是应用程序是否试图访问私有系统 API,这会使他们以后很难删除或破坏该 API。因此,从 Android 8 开始,在这种情况下,实现将返回 *NULL*。这是 Frida 为其自己的注入器的需求解决的挑战,但想要加载自己库的用户基本上只能靠自己。 + +从 Frida 12.5 开始,有一个全新的 JavaScript API 可以为您处理所有平台特定的怪癖: + +{% highlight js %} +const hooks = Module.load('/path/to/my-native-hooks.so'); +Interceptor.replace(Module.getExportByName('libc.so', 'read'), + hooks.getExportByName('replacement_read')); +{% endhighlight %} + +### Android + +我们在此版本中修复了许多 Android 特定的错误。例如,应用程序捆绑库上的 *Module.getExportByName()* 不再导致库在不同的基地址处第二次加载。仅此错误就足以确保您已升级所有设备并运行最新版本。 + +### iOS + +iOS Chimera 越狱也得到了支持,这要归功于 [Francesco Tamagni][] 的出色贡献。 + +### 其余部分 + +跨平台还有许多其他改进。 + +按时间顺序: + +- 子进程门控现在也可以在旧版本的 Windows 上工作。感谢 [Fernando Urbano][]! +- UNIX 操作系统上的可执行文件更小,因为它们不再导出任何动态符号。 +- Frida 的 agent 和 gadget 在加载到高地址(即 MSB 已设置)时不再在 32 位 Linux 上崩溃。 +- Linux/Android 的两个 *frida-helper-{32,64}* 二进制文件中只需要一个,对于没有跨架构支持的构建则不需要。这意味着更小的占用空间和更好的性能。 +- Linux/ARM64 终于得到支持,二进制文件作为发布过程的一部分上传。 +- 当我们的早期插桩无法在 Android 上检测 Zygote 时,我们现在提供有关 Magisk Hide 的提示。 + +### 12.5.1 中的变化 + +- 可以为每个脚本指定脚本运行时,弃用 *enable_jit()*。 + +### 12.5.2 中的变化 + +- Gadget 在 Linux 和 Android 上使用 V8 时在脚本加载时不再崩溃。非常感谢 [Leon Jacobs][] 报告并帮助追踪这个问题。 + +### 12.5.3 中的变化 + +- Android 链接器集成支持更多设备。 +- Android Java 集成不再在某些 arm64 设备上因"无效指令"而崩溃。感谢 [Jake Van Dyke][] 报告并帮助追踪这个问题。 +- 添加缺失的 SELinux 规则后支持 LineageOS 15.1。 + +### 12.5.4 中的变化 + +- Hook 不再能够干扰我们的 V8 页面分配器集成。 +- 在我们的 libc shim 中堵塞一个漏洞后,Android 稳定性大大提高。非常感谢 [Giovanni Rocca][] 报告并帮助追踪这个问题! + +### 12.5.5 中的变化 + +- Apple USB 设备在 Windows 上被正确检测。感谢 [@xiofee][]! + +### 12.5.6 中的变化 + +- Android Java 集成现在对 ART 的异常传递逻辑中的一个错误有一个解决方法,其中一个特定的代码路径假设当前线程上至少存在一个 Java 堆栈帧。然而,在纯本机线程(如 Frida 的 JS 线程)上并非如此。最简单的重现器是 *Java.deoptimizeEverything()* 后跟不存在的类名的 *Java.use()*。感谢 [Jake Van Dyke][] 报告并帮助追踪这个问题。 +- Android Java 集成在无法 TCP listen() 的进程中调用 *Java.deoptimizeEverything()* 时不再使进程崩溃。 +- Android Java 集成像以前一样支持 JNI 检查模式。 +- 除了 8 和 10 之外,还支持 Node.js 12,所有支持的平台都有预构建。 +- Node.js 绑定的 *enableDebugger()* 方法不再需要指定要监听的端口。 + +### 12.5.7 中的变化 + +- Android 拆卸在我们无法为崩溃报告目的 spawn *logcat* 的系统上不再崩溃。 +- Android 上更好的 *SuperSU* 集成拆卸逻辑。 +- Android Java 集成现在正确支持 JNI 检查模式,这大大提高了 Android ROM 兼容性。感谢 [@muhzii][] 报告并协助测试更改。 +- V8 后端拆卸不再遭受 use-after-free,并且在 WeakRef 绑定较晚时也不再崩溃。 + +### 12.5.8 中的变化 + +- Linux 子进程门控现在处理子进程更改架构,例如 32 位应用程序执行 fork+exec 以运行 64 位可执行文件。非常感谢 [@gebing][] 的修复。 +- 在 fork+exec 的情况下,如果不跟踪子进程,子进程门控不再死锁。感谢 [@gebing][] 的修复。 +- 模块导出查找不再在 Android 应用程序自己的模块上失败。 + +### 12.5.9 中的变化 + +- 我们的 libc shim 现在包括 *memcpy()*,使其可以安全地 hook。感谢 [Giovanni Rocca][] 调试并贡献修复。 +- *Interceptor.flush()* 现在即使线程暂时释放了 JS 锁也可以工作,例如在调用 *NativeFunction* 时。 +- Android Java 集成在 ART 异常传递期间不再间歇性崩溃,例如在 hook *ClassLoader.loadClass()* 时。感谢 [Jake Van Dyke][] 和 [Giovanni Rocca][] 帮助追踪这个问题。这个错误自从支持 ART 以来就一直存在,所以这个修复值得庆祝。🎉 +- Android Java 集成不再使无法启动 *JDWP* 传输的进程崩溃。 + + +[V8]: https://v8.dev/ +[Duktape]: https://duktape.org/ +[Gum]: https://github.com/frida/frida-gum +[JIT-less]: https://v8.dev/blog/jitless +[frida-compile]: https://github.com/oleavr/frida-agent-example +[TypeScript]: https://www.typescriptlang.org/ +[7.6.48]: https://chromium.googlesource.com/v8/v8/+/refs/tags/7.6.48 +[Stalker]: https://frida.re/docs/javascript-api/#stalker +[John Coates]: https://twitter.com/JohnCoatesDev +[Florian Märkl]: https://twitter.com/thestr4ng3r +[NativeFunction]: https://frida.re/docs/javascript-api/#nativefunction +[Francesco Tamagni]: https://twitter.com/bezjaje +[Fernando Urbano]: https://github.com/ineedblood +[Leon Jacobs]: https://twitter.com/leonjza +[Jake Van Dyke]: https://twitter.com/giantpune +[Giovanni Rocca]: https://twitter.com/iGio90 +[@xiofee]: https://github.com/xiofee +[@muhzii]: https://github.com/muhzii +[@gebing]: https://github.com/gebing diff --git a/_i18n/cn/_posts/2019-05-15-nowsecure-connect-2019.markdown b/_i18n/cn/_posts/2019-05-15-nowsecure-connect-2019.markdown new file mode 100644 index 00000000..bb2cac5c --- /dev/null +++ b/_i18n/cn/_posts/2019-05-15-nowsecure-connect-2019.markdown @@ -0,0 +1,12 @@ +--- +layout: news_item +title: 'NowSecure Connect 2019' +date: 2019-05-15 23:00:01 +0200 +author: oleavr +categories: [conference] +--- + +请务必查看将于 2019 年 6 月在华盛顿特区举行的 [NowSecure Connect 2019][] 会议。它将包含多个与 Frida 相关的演讲,我对阵容感到非常兴奋。 + + +[NowSecure Connect 2019]: https://info.nowsecure.com/Connect-19.html diff --git a/_i18n/cn/_posts/2019-05-28-frida-12-6-released.markdown b/_i18n/cn/_posts/2019-05-28-frida-12-6-released.markdown new file mode 100644 index 00000000..84cd60b5 --- /dev/null +++ b/_i18n/cn/_posts/2019-05-28-frida-12-6-released.markdown @@ -0,0 +1,164 @@ +--- +layout: news_item +title: 'Frida 12.6 发布' +date: 2019-05-28 02:00:00 +0200 +author: oleavr +version: 12.6 +categories: [release] +--- + +在过去几周内跨所有平台进行了一系列修复之后,我认为是时候再做一次小版本升级来引起对此版本的关注了。 + +有一个特定的修复值得特别提及。我们的 Android Java 集成中存在一个长期存在的错误,其中异常传递会间歇性地导致进程崩溃,堆栈跟踪中通常有 *GetOatQuickMethodHeader()*。向 [Jake Van Dyke][] 和 [Giovanni Rocca][] 致敬,感谢他们帮助追踪这个问题。自从支持 ART 以来,这个错误就一直存在,所以这个修复值得庆祝。🎉 + +我们的 V8 运行时也更加稳定,子进程门控比以往任何时候都更好,Android 设备兼容性大大提高等。 + +所以底线是这是有史以来发布的最稳定的 Frida 版本 —— 现在是确保您运行 Frida 12.6 的时候了。 + +享受吧! + +### 12.6.1 中的变化 + +- Android Java 集成中的异常传递修复在以解释器模式运行 VM 时引入了性能瓶颈,例如通过 *Java.deoptimizeEverything()*。例如,在运行 Android 9 的 Pixel 3 上从启动到登录屏幕运行 Dropbox 应用程序时,这将需要约 94 秒,现在需要约 6 秒。 + +### 12.6.2 中的变化 + +- 感谢 [Giovanni Rocca][] 贡献的修复,Android Java 集成现在支持更多 arm64 系统。 +- Android Java 集成再次支持同时被多个脚本使用。 + +### 12.6.3 中的变化 + +- 感谢 [Eugene Kolo][] 贡献的修复,*Java.choose()* 现在在 Android >= 8.1 上再次工作。 +- Android Java 集成 unhooking 现在再次工作。这也意味着 hook 在脚本卸载时被正确恢复。 +- Frida 现在可以与旧版本的 Frida 对话,从添加每脚本运行时选择之前的版本开始。 + +### 12.6.4 中的变化 + +- 构建系统在所有平台上恢复正常。 + +### 12.6.5 中的变化 + +- Linux 线程枚举现在在 x86-64 上正常工作。 +- Stalker 终于处理可重启的 Linux 系统调用。 + +### 12.6.6 中的变化 + +- Android Java 集成在 32 位 ARM 上恢复完全工作状态。 + +### 12.6.7 中的变化 + +- 现在支持最新的 Chimera iOS 越狱;确认在 *1.0.8* 上工作。 +- Linux 注入器处理 libc 名称不明确的目标进程,这在 Android 上经常是一个问题。 + +### 12.6.8 中的变化 + +- *ObjC.Object* 现在提供 *$moduleName*,用于确定哪个模块拥有给定的类。感谢 [David Weinstein][] 贡献这个巧妙的功能! + +### 12.6.9 中的变化 + +- 现在完全支持最新的 unc0ver 越狱。 +- 早期插桩逻辑已得到改进,以完全支持 iOS 12。感谢 [Francesco Tamagni][] 帮助完成这些棘手的更改。 +- iOS 12 上的内存范围枚举现在更可靠,因为属于线程的内存范围现在被正确隐藏。 +- 隐藏的内存范围现在被压缩,使查找更快。 +- 子进程门控能够保持子进程超过 25 秒。以前这些会在那个意外超时后自动恢复。这也影响 Android 上的早期插桩,因为它建立在子进程门控之上。 +- 感谢 [John Coates][] 的出色贡献,Swift 绑定支持 RPC 并已移至 Swift 5.0。 + +### 12.6.10 中的变化 + +- 内存范围枚举现在在所有平台上都是可靠的。当删除最终拆分现有范围时,存在一个长期存在的错误。 + +### 12.6.11 中的变化 + +- 感谢 [CodeColorist][] 的出色修复,iOS >= 12 上的应用程序枚举包括图标。 +- 感谢 [gebing][] 的巧妙贡献,Gadget 学会了如何检测 Android 应用程序的可执行文件和包名称。 +- 内存范围隐藏得到了影响 Windows 用户的关键修复。 + +### 12.6.12 中的变化 + +- *frida-inject* 工具现在支持通过 *-P/--parameters* 将参数传递给脚本。感谢 [Eugene Kolo][] 贡献这个巧妙的功能。 +- 当脚本卸载阻塞时,子进程门控不再死锁。感谢 [Ioannis Gasparis][] 帮助追踪这个问题。 +- 子进程门控更可靠,因为 Frida 现在在更高的范围内分配文件描述符,以避免它们在 *fork()+exec()* 期间被调用 *dup2()* 的应用程序关闭。这通常会在 Android 上的应用程序调用 *Runtime.exec()* 时发生。感谢 [Ioannis Gasparis][] 帮助追踪这个问题。 +- 感谢 [Muhammed Ziad][] 的一系列出色贡献,痛苦的 Android NDK 升级到 r20 已完成。 +- 错误处理得到改进,以避免在由于缺乏权限而无法初始化的场景中崩溃。感谢 [pancake][] 报告。 +- 长期以来一直在分支中的 iOS 本机 lockdown 集成终于合并了。它未完成并被认为是不稳定的 API,但由于正在进行的重大重构而不得不合并。 +- *Stalker* 现在允许从 *transform* 回调中调用 *unfollow()*,而不是像以前那样使进程崩溃。感谢 [Giovanni Rocca][] 帮助修复这个问题。 +- Gadget 的 Android 包名称检测逻辑得到改进,以处理以前未考虑的一个边缘情况。感谢 [xiaobaiyey][] 报告并建议修复。 +- *Java.registerClass()* API 得到改进,以支持指定超类,以及对该 API 和处理带泛型的数组的一系列修复。非常感谢 [gebing][] 提供的这些出色改进。 + +### 12.6.13 中的变化 + +- Agent 和 Gadget 中的构造函数/析构函数现在终于正确排序,我们的 libc shim 的内存分配器 hack 可以被删除。这些脆弱的 hack 由于工具链的 *libgcc* 中的微妙变化而在 Android 上的 32 位 ARM 进程中以新的和丰富多彩的方式破坏。 +- 我们的 libc shim 现在还处理 *__cxa_atexit()* 和 *atexit()*,其中前者对于避免泄漏至关重要。 + +### 12.6.14 中的变化 + +- 感谢 [gebing][] 贡献的出色改进,*Java.registerClass()* API 得到改进,以支持用户定义的构造函数和字段。 +- 临时文件现在在所有平台上都被清理。 +- frida-server 在 Windows 上处理 Ctrl+C 以支持优雅关闭而不留下临时文件。 + +### 12.6.15 中的变化 + +- *NativePointerValue*,即除了 *NativePointer* 之外还支持传递具有 *handle* 属性的对象,现在在所有地方再次工作。 + +### 12.6.16 中的变化 + +- *frida-gadget-ios* 元包现在是无依赖的。 + +### 12.6.17 中的变化 + +- 崩溃报告器集成现在与 iOS 12.4 兼容。 +- Java 集成得到改进,在调用替换方法时急切地释放全局句柄,以避免耗尽句柄。感谢 [gebing][] 提供的这个不错的改进。 +- 添加了 *Java.retain()* 以允许在替换的 Java 方法之外存储 *this* 以供以后使用。 +- 对旧版本 Android 的支持现在应该稍微好一点。 +- 如果目标进程在向其注入库时死亡,Frida 不再在 i/macOS 上崩溃。 +- 感谢 [Jon Wilson][] 的出色贡献,支持 MIPS64。 +- 感谢 [gebing][] 的修复,*Memory.patchCode()* 不再在 *android-arm64* 上崩溃。 +- 拦截已拦截的 Thumb 函数现在正常工作。 +- Frida 在 iOS 上的进程生命周期早期记录日志时不再崩溃。 +- 简单的 *ModuleApiResolver* 查询现在速度极快。 +- *Duktape* 运行时不再包括有问题的 *Reflect* 内置。 +- *Interceptor* C API 被重构以改进命名。 + +### 12.6.18 中的变化 + +- Gadget 二进制文件在所有平台上再次被剥离。 + +### 12.6.19 中的变化 + +- 所有 API 现在都是可取消的。Python 和 Node.js 语言绑定支持传递 *Cancellable* 对象。其余语言绑定像以前一样工作,非常欢迎贡献。 + +### 12.6.20 中的变化 + +- *DeviceManager.find_device()* 在启动时不再崩溃。 + +### 12.6.21 中的变化 + +- *Future.wait_async()* 在最后一刻取消时不再崩溃。 + +### 12.6.22 中的变化 + +- 状态管理得到改进,以允许在已处置状态下也使用 *eternalize()* 和 *post()*。 +- *dispose()* RPC 导出现在传递一个指定原因的参数,以便能够区分 *unload*、*exit* 和 *exec*。 +- 如果 exec 转换失败,脚本现在可以再次 *dispose()*。 +- *frida-inject* CLI 工具得到改进,以在分离时退出。 +- *RpcClient* 在 *post_rpc_message()* 失败时不再崩溃。 +- *Fruity* 和 *Droidy* 后端现在在 iOS 和 Android 上被禁用,以减少这些后端不适用的平台上的占用空间大小。 + +### 12.6.23 中的变化 + +- 当 *HostSession* 在请求中途丢失时,Frida 不再崩溃。 + + +[Jake Van Dyke]: https://twitter.com/giantpune +[Giovanni Rocca]: https://twitter.com/iGio90 +[Eugene Kolo]: https://twitter.com/eugenekolo +[David Weinstein]: https://twitter.com/insitusec +[Francesco Tamagni]: https://twitter.com/bezjaje +[John Coates]: https://twitter.com/JohnCoatesDev +[CodeColorist]: https://twitter.com/CodeColorist +[gebing]: https://github.com/gebing +[Ioannis Gasparis]: https://github.com/igasparis +[Muhammed Ziad]: https://github.com/muhzii +[pancake]: https://twitter.com/trufae +[xiaobaiyey]: https://github.com/xiaobaiyey +[Jon Wilson]: https://github.com/jonwilson030981 diff --git a/_i18n/cn/_posts/2019-09-18-frida-12-7-released.markdown b/_i18n/cn/_posts/2019-09-18-frida-12-7-released.markdown new file mode 100644 index 00000000..2c42f579 --- /dev/null +++ b/_i18n/cn/_posts/2019-09-18-frida-12-7-released.markdown @@ -0,0 +1,460 @@ +--- +layout: news_item +title: 'Frida 12.7 发布' +date: 2019-09-18 23:00:00 +0200 +author: oleavr +version: 12.7 +categories: [release] +--- + +这次只有一个新功能,但这是一个大功能。我们要解决房间里的大象:性能。 + +虽然 Frida 的检测核心 Gum 是用 C 编写的,并且可以从 C 使用,但大多数用例最好使用其 JavaScript 绑定。 + +然而,在某些情况下,性能会成为问题。即使使用我们基于 V8 的运行时,这意味着您的 JavaScript 将在运行时进行分析,并根据热点所在进行优化……(顺便说一句,这太棒了 —— V8 确实是一项令人印象深刻的工程壮举!) + +……进入和离开 JavaScript VM 还是有一点代价的。在 iPhone 5S 上,如果您使用 *Interceptor.attach()* 并且只指定 *onEnter* 并将其留空,这可能相当于大约 6 微秒。 + +这听起来可能并不多,但如果一个函数被调用一百万次,它将增加 6 秒的开销。也许 hook 只需要做一些非常简单的事情,所以大部分时间实际上都花在了进入和离开 VM 上。 + +当需要将回调传递给 API 时也存在同样的问题,API 会遍历可能数百万个项目,并需要为每个项目调用回调。回调可能只查看一个字节并收集符合特定条件的少数项目。 + +天真地,人们可以继续使用 NativeCallback 来实现该回调,但很快就会发现这根本无法扩展。 + +或者,您可能正在编写一个 fuzzer,并且需要在紧密循环中调用 NativeFunction,进入/离开 VM 加上 libffi 的成本就会增加。 + +除了用 C 编写整个 agent 之外,人们可以继续构建本机库,并使用 *Module.load()* 加载它。这可行,但意味着必须为每个架构编译它,部署到目标等。 + +另一种解决方案是使用 X86Writer/Arm64Writer/etc. API 在运行时生成代码。这也是痛苦的,因为每个要支持的架构都需要做相当多的工作。但直到现在,这是在诸如 [frida-java-bridge][] 之类的模块中使用的唯一可移植选项。 + +但现在我们终于有了更好的东西。进入 **CModule**: + +![CModule Hello World](/img/cmodule-hello-world.png "CModule Hello World") + +它获取 C 源代码字符串并将其编译为机器代码,直接存入内存。这是使用 [TinyCC][] 实现的,这意味着此功能仅为 Frida 增加了约 100 kB 的占用空间。 + +如您所见,任何全局函数都会自动导出为 NativePointer 属性,其名称与 C 源代码中的完全相同。 + +而且,它很快: + +![CModule Speed](/img/cmodule-speed.png "CModule Speed") + +(在 Intel i7 @ 3.1 GHz 上测量。) + +我们还可以将此新功能与 Interceptor 等 API 结合使用: + +{% highlight js %} +const m = new CModule(` +#include + +#define EPERM 1 + +int +open (const char * path, + int oflag, + ...) +{ + GumInvocationContext * ic; + + ic = gum_interceptor_get_current_invocation (); + ic->system_error = EPERM; + + return -1; +} +`); + +const openImpl = Module.getExportByName(null, 'open'); + +Interceptor.replace(openImpl, m.open); +{% endhighlight %} + +(请注意,此示例和以下示例使用现代 JavaScript 功能,如模板文字,因此它们要么需要在我们的 V8 运行时上运行,要么使用 [frida-compile][] 编译。) + +我们还可以将其与 *Interceptor.attach()* 结合使用: + +{% highlight js %} +const openImpl = Module.getExportByName(null, 'open'); + +Interceptor.attach(openImpl, new CModule(` + #include + #include + + void + onEnter (GumInvocationContext * ic) + { + const char * path; + + path = gum_invocation_context_get_nth_argument (ic, 0); + + printf ("open() path=\\"%s\\"\\n", path); + } + + void + onLeave (GumInvocationContext * ic) + { + int fd; + + fd = (int) gum_invocation_context_get_return_value (ic); + + printf ("=> fd=%d\\n", fd); + } +`)); +{% endhighlight %} + +耶。虽然这最后一个特定的例子实际上写入了目标进程的 *stdout*,这对于调试来说很好,但可能并不那么有用。 + +但是,我们可以通过回调 JavaScript 来解决这个问题。让我们看看那可能是什么样子: + +{% highlight js %} +const openImpl = Module.getExportByName(null, 'open'); + +Interceptor.attach(openImpl, new CModule(` + #include + + extern void onMessage (const gchar * message); + + static void log (const gchar * format, ...); + + void + onEnter (GumInvocationContext * ic) + { + const char * path; + + path = gum_invocation_context_get_nth_argument (ic, 0); + + log ("open() path=\\"%s\\"", path); + } + + void + onLeave (GumInvocationContext * ic) + { + int fd; + + fd = (int) gum_invocation_context_get_return_value (ic); + + log ("=> fd=%d", fd); + } + + static void + log (const gchar * format, + ...) + { + gchar * message; + va_list args; + + va_start (args, format); + message = g_strdup_vprintf (format, args); + va_end (args); + + onMessage (message); + + g_free (message); + } +`, { + onMessage: new NativeCallback(messagePtr => { + const message = messagePtr.readUtf8String(); + console.log('onMessage:', message); + }, 'void', ['pointer']) +})); +{% endhighlight %} + +然而,这只是一个玩具示例:以这种方式做实际上会违背用 C 编写 hook 以提高性能的目的。真正的实现可能会在获取 [GLib.Mutex][] 后附加到 [GLib.Array][],并通过回调 JS 定期刷新缓冲数据。 + +就像可以从 C 调用 JavaScript 函数一样,我们也可以在两个领域之间共享数据: + +{% highlight js %} +const calls = Memory.alloc(4); + +const openImpl = Module.getExportByName(null, 'open'); + +Interceptor.attach(openImpl, new CModule(` + #include + + extern volatile gint calls; + + void + onEnter (GumInvocationContext * ic) + { + g_atomic_int_add (&calls, 1); + } +`, { calls })); + +setInterval(() => { + console.log('Calls so far:', calls.readInt()); +}, 1000); +{% endhighlight %} + +目前我们没有任何关于内置 C API 的文档,但您可以浏览 [frida-gum/bindings/gumjs/runtime/cmodule][] 中的头文件以获得概览。将函数名称放入互联网搜索引擎中以查找非 Frida API(如 GLib)的文档。 + +目的是只公开标准 C 库、GLib、JSON-GLib 和 Gum API 的最小子集;以尽量减少膨胀并最大化性能。我们包含的内容应该是通过调用 JS 无法实现的,或者以这种方式实现成本过高的。 + +将 JS 端视为操作系统,您插入其中的函数是系统调用;并且仅使用 CModule 来 hook 热点函数或实现高性能粘合代码,如传递给性能敏感 API 的回调。 + +还要记住,TinyCC 生成的机器代码不如 Clang 或 GCC 的效率高,因此计算昂贵的算法实际上可能在 JavaScript 中实现得更快。(当使用我们基于 V8 的运行时。)但对于 hook 和粘合代码,这种差异并不显着,如果您需要优化内部循环,您总是可以使用例如 Arm64Writer 生成机器代码并插入您的 CModule。 + +一个重要的警告是所有数据都是只读的,因此可写全局变量应声明为 *extern*,使用例如 *Memory.alloc()* 分配,并通过构造函数的第二个参数作为符号传入。(就像我们在上一个示例中对 `calls` 所做的那样。) + +您可能还需要在 CModule 被销毁时初始化事物并清理它们 —— 例如因为脚本被卸载 —— 我们为此类目的提供了几个生命周期 hook: + +{% highlight js %} +const cm = new CModule(` +#include + +void +init (void) +{ + printf ("init\\n"); +} + +void +finalize (void) +{ + printf ("finalize\\n"); +} +`); + +cm.dispose(); // or wait until it gets GCed or script unloaded +{% endhighlight %} + +无论如何,这篇文章越来越长,但在我们结束之前,让我们看看如何将 CModule 与 Stalker API 一起使用: + +{% highlight js %} +const cm = new CModule(` +#include + +static void on_ret (GumCpuContext * cpu_context, + gpointer user_data); + +void +transform (GumStalkerIterator * iterator, + GumStalkerOutput * output, + gpointer user_data) +{ + cs_insn * insn; + + while (gum_stalker_iterator_next (iterator, &insn)) + { + if (insn->id == X86_INS_RET) + { + gum_x86_writer_put_nop (output->writer.x86); + gum_stalker_iterator_put_callout (iterator, + on_ret, NULL, NULL); + } + + gum_stalker_iterator_keep (iterator); + } +} + +static void +on_ret (GumCpuContext * cpu_context, + gpointer user_data) +{ + printf ("on_ret!\n"); +} +`); + +const mainThread = Process.enumerateThreads()[0]; + +Stalker.follow(mainThread.id, { + transform: cm.transform, + data: ptr(1337) +}); +{% endhighlight %} + +这展示了如何用 C 实现 transform 回调和 callout,但您也可以使用混合方法,其中用 JS 编写 transform 回调,只用 C 编写部分 callout。 + +还值得注意的是,我重写了 *ObjC.choose()* 以使用 CModule,现在它的速度大约快了 100 倍。当在 iPhone 6S 上的 Twitter 应用程序的登录屏幕上测试它时,这从大约需要 5 秒变为现在只需要约 50 毫秒。 + +因此,我希望您会喜欢这个版本。很高兴看到您将使用新的 CModule API 构建什么样的东西。我真正期待的一件事是改进我们的 REPL 以支持在 *.js* 旁边加载 *.c* 文件,以便进行快速原型设计。 + +享受吧! + + +### 12.7.0 中的变化 + +- 由 TinyCC 提供支持的全新 CModule API。(您刚刚读到了。) +- TinyCC 得到改进,以支持 macOS/x86 上的 Apple ABI。 +- *Stalker.exclude()* 现在暴露给 JS,以便能够将特定内存范围标记为排除。这对于提高性能和减少噪音很有用。 +- 感谢 [@gebing][] 的巧妙贡献,现在支持对 *Java.use()* 的并发调用。 +- *hexdump()* 实现得到改进,将 *length* 选项限制为 ArrayBuffer 的长度,感谢 [@gebing][] 的另一个巧妙贡献。 + +### 12.7.1 中的变化 + +- 更多 CModule 好东西,包括 [GLib.String][]、[GLib.Timer][] 和 [Json.Builder][]。 +- TinyCC 得到改进,以支持 iOS/arm64 上的 Apple ABI。 +- *ObjC.choose()* 使用 CModule 重写,现在快了约 100 倍。 + +### 12.7.2 中的变化 + +- CModule 获得了一些缺失的引用计数 API。 + +### 12.7.3 中的变化 + +- CModule 内存范围现在被正确隐藏。 +- V8 垃圾收集器现在被告知外部分配的 CModule 内存,以便它可以更好地决定何时进行 GC。 +- 附加到 CModule 的符号现在也在 V8 运行时中正确保持活动状态;并且 CModule 本身不会无限期地保持活动状态(或直到脚本卸载)。 +- 添加了 *CModule.dispose()* 用于急切地清理内存。 + +### 12.7.4 中的变化 + +- *frida-inject* 工具现在支持 *spawn()*。感谢 [@hunterli][] 贡献这个巧妙的功能。 +- 我们的 V8 运行时在 i/macOS 上不再死锁,当 *thread_suspend()* 在仍然持有 JS 锁的情况下被调用时,就像 *Stalker.follow()* 在被要求跟踪另一个线程时间接做的那样。 + +### 12.7.5 中的变化 + +- 全新的通道 API,用于建立到系留 iOS 或 Android 设备的 TCP 连接,以及与系留 iOS 设备上的 lockdown 服务对话。 +- *DeviceManager.find_device()* 及其兄弟方法背后的超时逻辑现在正常工作。 +- *java.lang.Class* 的 Java 编组现在正常工作,并且实例字段也可以在不需要实例的情况下进行内省。感谢 [@gebing][] 贡献这些巧妙的修复! + +### 12.7.6 中的变化 + +- Android 链接器现在在 Android 10 上被正确检测。 +- 我们的 Android SELinux 策略修补程序现在也处理像三星 S10 这样的设备,感谢 [@cbayet][] 的巧妙贡献。 +- *frida-inject* 工具现在支持 *-D/--device* 以使用非本地设备。 +- 我们现在有更好的错误处理,以避免在 i/macOS 进程在早期插桩期间意外终止时崩溃。 +- iOS 崩溃报告器集成更加强大,感谢 [@mrmacete][] 贡献的一些很棒的修复。他的一个修复还确保对同一消息类型的 *recv().wait()* 的并行调用不会导致无限等待。 +- Linux/arm64 上现在支持跟踪线程创建。感谢 [@alvaro_fe][] 的这个很棒的贡献! +- V8 运行时的 WebAssembly 支持在非 iOS 上也再次工作。 +- *Gum.DarwinModule* API 现在是跨平台 Gum API 的一部分。用于在非 Apple 系统上解析 Mach-O 文件。 + +### 12.7.7 中的变化 + +- 永恒化的 agent 现在在最后一个会话关闭时保留,这意味着只要 *HostSession* 端(例如 frida-server)存在,它们就可以重用。这意味着在很多情况下可以避免 frida-agent 的额外副本。感谢 [@mrmacete][] 提供的这个很棒的改进。 +- Java bridge 在方法返回 *this* 时不再触发 use-after-free。 +- 我们的 Android SELinux 策略修补程序不再在旧版本的 Android 上打印警告。这个无害但令人困惑的回归是由上一版本针对三星 S10 ROM 的修复引入的。 +- 更好的 SELinux 相关错误消息。 +- 对 iOS/arm64e 的基本支持。 + +### 12.7.8 中的变化 + +- 感谢 [@Alien-AV][] 的精彩贡献,Android 10 支持刚刚登陆我们的 Java bridge。 +- 更好的 Android 应用程序 spawn() 处理,其中 *activity* 参数可用于应用程序没有启动器活动的情况。这个巧妙的改进是由 [@muhzii][] 贡献的。 +- 感谢 [@timstrazz][] 的优雅贡献,Android 链接器搜索逻辑变得面向未来。 +- 大规模改进了 iOS 上的容错性:我们的 launchd agent 现在在卸载时杀死挂起的进程。这意味着 frida-server 死亡不会让进程卡在挂起状态。感谢 [@mrmacete][] 提供的这个很棒的改进。 + +### 12.7.9 中的变化 + +- 在上一版本中潜入最后一分钟的构建回归后,我们在 macOS 上恢复了业务。 + +### 12.7.10 中的变化 + +- *MemoryAccessMonitor* 现在在所有平台上可用,甚至 Duktape 运行时。感谢 [@alvaro_fe][] 提供的这些很棒的改进。 +- Android 链接器检测再次正常工作。(12.7.8 中引入的回归。) +- Gadget 得到改进,支持在 i/macOS 上通过其构造函数传递配置。 +- Gadget 的系统循环集成 —— 仅在 i/macOS 上实现 —— 被删除,以避免在某些情况下出现未定义的行为。 + +### 12.7.11 中的变化 + +- Frida 不再在没有 vDSO 的 Android 进程中崩溃。(12.7.8 中引入的回归。) +- 解析 Mach-O 图像时更好的错误处理。 +- 在 i/macOS 上恢复异常时正确处理 ARM 与 Thumb。感谢 [@alvaro_fe][]! +- CModule 的 JSON-GLib 头文件现在是自包含的,正如它应该的那样。 + +### 12.7.12 中的变化 + +- 功能齐全的 iOS lockdown 集成和统一设备,因此基于 Frida 的工具不需要太担心 jailed 与 jailbroken。当与受限 iOS 设备交互时,Gadget 现在会自动注入,无需重新打包应用程序,它只需要是可调试的。 +- Frida 终于能够在 Windows 上检测最近的 iOS 设备。 +- V8 中的错误被追踪并从上游反向移植修复。感谢 [@mrmacete][] 追踪这个问题! + +### 12.7.13 中的变化 + +- 更好地处理 *frida-objc-bridge* 中的结构和联合。感谢 [@gebing][]! +- 我们的 Node.js 绑定现在还公开 *Crash* 和 *CrashParameters* 的类型定义。 +- 尝试附加到受限 iOS 上的系统会话会提前抛出并带有更清晰的错误消息。 +- iOS 开发者磁盘映像相关的错误消息为了保持一致性进行了调整。 + +### 12.7.14 中的变化 + +- Frida 现在确保在 Android >= 10 上访问代码之前它是可读的。这是功能齐全的 Android 10 支持的最后一块缺失拼图。能够检测系统进程意味着早期插桩 —— 即 spawn() —— 工作,并且启动 frida-server 不会因其尝试预加载而导致 *system_server* 崩溃,因此第一次 *spawn()* 将很快。感谢 [@Alien-AV][] 和 [@esanfelix][] 进行的痛苦研究,使得在一个深夜的星期六晚上实现解决方案成为可能。:-) + +### 12.7.15 中的变化 + +- Node.js 绑定还公开 *Crash* 类型的"summary"字段。 + +### 12.7.16 中的变化 + +- *frida-gadget-ios* 元包附带类型定义,因此可以从 TypeScript 使用。 +- Node.js 绑定为 *Stdio* 和 *ChildOrigin* 提供适当的类型。 + +### 12.7.17 中的变化 + +- 对受限 iOS 的更强大支持:在 attach() 之后但在 resume() 之前调用 kill() 现在正常工作。 +- Frida 现在尽可能直接与远程 iOS/Android agent 通信。我们通过建立到 frida-server 的新 TCP 连接并将其文件描述符传递给 agent 来实现这一点。这意味着 frida-server 可以从数据路径中移除自身,从而提高性能和可靠性。 +- 连接到 frida-server 以 spawn() 进程,但在有机会 resume() 或 kill() 该进程之前断开连接的客户端,将不再让这些孤儿处于不确定状态。我们现在跟踪生成的进程,如果客户端突然断开连接,则 kill() 它们。 +- 我们不再使 Android 10 上的 Zygote 崩溃。原来是缺少 SELinux 规则。 +- 我们的 TCP 套接字现在设置了 *TCP_NODELAY*,并且我们还支持除了 TCP 之外使用 UNIX 套接字与远程 frida-server 或 frida-gadget 通信。 +- NativeFunction 现在支持可变参数函数,以避免需要为每个唯一的参数列表签名创建一个实例。感谢 [@gebing][]! + +### 12.7.18 中的变化 + +- Node.js 绑定终于在 UNIX 上链接所需的 OpenSSL 符号,而不是依赖运气,我们通常会在已经加载了另一个全局可见 *且* ABI 兼容 (!) 的 OpenSSL 的进程中结束。 + +### 12.7.19 中的变化 + +- 我们现在还在 V8 运行时中正确处理无参数 NativeFunction 上的 apply()。感谢 [@taviso][] 报告这个长期存在的错误。 +- NativeFunction 在 call() 和 apply() 中对可选参数的处理现在表现得像内置对应物一样。 + +### 12.7.20 中的变化 + +- Frida 现在支持明文和加密的 iOS lockdown 通道,通过在通道地址后附加 "?tls=handshake-only" 来保留对"仅 TLS 握手后明文"风格通道的支持。感谢,[@mrmacete][]! +- 配对的 lockdown 通道本身可以通过使用 "lockdown:" 作为通道地址来访问。感谢 [@mrmacete][]! + +### 12.7.21 中的变化 + +- 我们现在在 checkra1n 越狱上支持 iOS 13。(受限 iOS 13 已经支持。) + +### 12.7.22 中的变化 + +- 我们的 iOS 包脚本启动守护程序逻辑现在与 checkra1n 兼容,因此不必手动启动/停止 frida-server。 + +### 12.7.23 中的变化 + +- 增强了对 checkra1n 越狱的支持:Stalker 现在快得多,因为它利用了 RWX 页面。 +- 在没有 RWX 支持的越狱上在 iOS 上使用 Stalker 时的稳定性改进。感谢 [@mrmacete][]! +- CModule 现在与更多 iOS 越狱版本兼容。感谢,[@mrmacete][]! +- CModule 运行时支持使用 ModuleMap 对象。 +- 感谢 [Jon Wilson][] 的出色贡献,支持 ARMBE8。 +- Frida 现在在 i/macOS 上 spawn() 时使父文件描述符对子进程可用。这与 Linux 上的当前行为一致。感谢 [@wizche][]! + +### 12.7.24 中的变化 + +- 还为 Node.js v13 提供了 Node.js 预构建。 + +### 12.7.25 中的变化 + +- 日志处理程序 API 在 Python 和 Node.js 绑定中进行了大修,作为关键修复的一部分:Node.js setter 的类型与 getter 不同,因为它还允许 *null*。这种不一致导致最近的 TypeScript 编译器版本对其窒息。感谢 [@mrmacete][]! +- V8 平台集成中不再有时间戳截断。感谢报告,[@DaveManouchehri][]! +- 我们的 *Module.enumerateSymbols()* API 在可用时提供"size"属性,即目前仅在 Linux/Android 上。感谢 [@DaveManouchehri][]! +- *Java.use(name, { cache: 'skip' })* 现在可用于绕过缓存。在处理多个类加载器和冲突的类名时很有用。感谢 [@ChaosData][] 和 [@H4oK3][]! + +### 12.7.26 中的变化 + +- Stalker 现在支持临时重新激活,以允许从排除的内存范围内部跟踪代码。 +- NativeFunction 获得了一个全新的选项 `traps: 'all'`,即使 Frida 自己的内存范围被标记为排除,也允许跟踪调用。 +- 使用 Yama 时,Linux 上的线程枚举终于可以工作了。感谢 [Jon Wilson][]! + + +[frida-java-bridge]: https://github.com/frida/frida-java-bridge +[TinyCC]: https://bellard.org/tcc/ +[frida-compile]: https://github.com/oleavr/frida-agent-example +[GLib.Array]: https://developer.gnome.org/glib/stable/glib-Arrays.html +[GLib.Mutex]: https://developer.gnome.org/glib/stable/glib-Threads.html#GMutex +[frida-gum/bindings/gumjs/runtime/cmodule]: https://github.com/frida/frida-gum/tree/master/bindings/gumjs/runtime/cmodule +[@gebing]: https://github.com/gebing +[GLib.String]: https://developer.gnome.org/glib/stable/glib-Strings.html +[GLib.Timer]: https://developer.gnome.org/glib/stable/glib-Timers.html +[Json.Builder]: https://developer.gnome.org/json-glib/stable/JsonBuilder.html +[@hunterli]: https://github.com/hunterli +[@cbayet]: https://github.com/cbayet +[@mrmacete]: https://twitter.com/bezjaje +[@alvaro_fe]: https://twitter.com/alvaro_fe +[@Alien-AV]: https://github.com/Alien-AV +[@muhzii]: https://github.com/muhzii +[@timstrazz]: https://twitter.com/timstrazz +[@esanfelix]: https://twitter.com/esanfelix +[@taviso]: https://twitter.com/taviso +[Jon Wilson]: https://github.com/jonwilson030981 +[@wizche]: https://twitter.com/wizche +[@DaveManouchehri]: https://twitter.com/DaveManouchehri +[@ChaosData]: https://github.com/ChaosData +[@H4oK3]: https://github.com/H4oK3 diff --git a/_i18n/cn/_posts/2019-12-18-frida-12-8-released.markdown b/_i18n/cn/_posts/2019-12-18-frida-12-8-released.markdown new file mode 100644 index 00000000..78a1193a --- /dev/null +++ b/_i18n/cn/_posts/2019-12-18-frida-12-8-released.markdown @@ -0,0 +1,271 @@ +--- +layout: news_item +title: 'Frida 12.8 发布' +date: 2019-12-18 20:15:00 +0200 +author: oleavr +version: 12.8 +categories: [release] +--- + +准备好迎接一个激动人心的新版本。这次我们将对我们的 [Stalker][] 引擎给予一些期待已久的关爱。它已经存在了大约十年,但直到 2017 年底的 Frida 10.5,我们才 [started][] 释放其巨大的潜力。 + +到目前为止,我们能够 Stalker.follow() 现有线程,不仅观察它们,还可以以任何我们喜欢的方式改变它们的指令流。它还可以与 Interceptor 结合使用,在战略点之间检测当前线程。这使我们能够构建诸如 [AirSpy][] 之类的工具。 + +但是,如果我们想要 Stalker.follow() 一个 NativeFunction 调用呢?这看起来真的很简单,但可重入性使这真的很难。很容易最终跟踪执行进入例如我们的私有堆,并最终需要为检测本身分配内存……各种有趣的场景让人难以理解。 + +我们处理这个问题的方法是教 Stalker 排除某些内存范围,这样如果它看到一个调用到这样的位置,它将简单地发出一个调用指令到那里,而不是跟踪执行。所以我们所做的是自动排除 frida-agent 自己的内存范围,这样我们就不必处理任何可重入性的疯狂。 + +我们还特别注意尝试 Stalker.follow() 当前线程的情况,所以我们将该工作排队,直到我们即将离开我们的运行时并转换回用户代码(或者在 JS 线程的情况下,我们的主循环)。 + +这仍然留下了如何将 Stalker 与 NativeFunction 结合使用的大问题。我们现在终于可以把这个问题抛在脑后了: + +{% highlight js %} +const open = new NativeFunction( + Module.getExportByName(null, 'open'), + 'int', ['pointer', 'int'], + { traps: 'all' } +); + +Stalker.follow({ + events: { + call: true + }, + onReceive(e) { + console.log(JSON.stringify(Stalker.parse(e))); + } +}); + +const fd = open(Memory.allocUtf8String('/foo/bar'), 0); +console.log('open() =>', fd); +{% endhighlight %} + +通过在 NativeFunction 上设置 `traps: 'all'` 选项,当从 Stalker 暂时暂停的线程调用时,它将重新激活 Stalker,因为它正在调用一个被排除的范围 —— 这里就是这种情况,因为 frida-agent 的所有代码都被标记为排除。 + +我们还可以为 Objective-C 方法实现相同的目标: + +{% highlight js %} +Stalker.follow({ + events: { + call: true + }, + onReceive(e) { + console.log(JSON.stringify(Stalker.parse(e))); + } +}); + +const NSAutoreleasePool = ObjC.classes.NSAutoreleasePool; +const NSFileManager = ObjC.classes.NSFileManager; + +const fileExistsAtPath = NSFileManager['- fileExistsAtPath:'] + .clone({ traps: 'all' }); + +const pool = NSAutoreleasePool.alloc().init(); +try { + const manager = NSFileManager.defaultManager(); + const result = fileExistsAtPath.call(manager, '/foo/bar'); + console.log('fileExistsAtPath() =>', result); +} finally { + pool.release(); +} +{% endhighlight %} + +以及 Android 上的 Java 方法: + +{% highlight js %} +Stalker.follow({ + events: { + call: true + }, + onReceive(e) { + console.log(JSON.stringify(Stalker.parse(e))); + } +}); + +Java.perform(() => { + const JFile = Java.use('java.io.File'); + const exists = JFile.exists.clone({ traps: 'all' }); + + const file = JFile.$new('/foo/bar'); + const result = exists.call(file); + console.log('exists() =>', result); +}); +{% endhighlight %} + +耶。也就是说,这些例子几乎没有触及使用 Stalker 可能实现的表面。真正酷的用例之一是进程内模糊测试,[frida-fuzz][] 就是一个很好的例子。还有很多其他用例,例如逆向、测量代码覆盖率、用于测试目的的故障注入、hook 内联系统调用等。 + +所以这就是这个版本的主要故事。想要感谢 [@andreafioraldi][] 提供的出色错误报告和帮助测试这些棘手的更改。 + +### 总结 + +值得一提的一个很酷的新功能是新的 `ArrayBuffer.wrap()` API,它允许您方便高效地访问内存区域,就像它们是 JavaScript 数组一样: + +{% highlight js %} +const header = Memory.alloc(16); + +const bytes = new Uint8Array(ArrayBuffer.wrap(header, 16)); +bytes[0] = 1; +bytes[0] += 2; +bytes[1] = 2; + +console.log(hexdump(header, { length: 16, ansi: true })); +console.log('First byte is:', bytes[0]); +{% endhighlight %} + +这意味着您可以将直接内存访问权交给 JavaScript API,而无需将内存复制进出运行时。唯一的缺点是坏指针不会导致 JS 异常,而会使进程崩溃。 + +我们现在还允许您通过 ArrayBuffer 上的新 `unwrap()` 方法访问任何 ArrayBuffer 的后备存储。这方面的一个示例用例是使用现有模块(如 [frida-fs][])时,您会获得一个 ArrayBuffer,然后希望将其传递给本机代码。 + +感谢 [@DaveManouchehri][] 贡献了 ArrayBuffer.wrap() API 的第一个草案,也非常感谢 [@CodeColorist][] 建议并帮助塑造 unwrap() 功能。 + +### 12.8.0 中的变化 + +- Stalker 重新激活正常工作。 +- Stalker 线程生命周期得到正确处理。在 i/macOS 上跟踪线程直到其死亡时也不再崩溃。 +- Stalker 中更安全的垃圾收集逻辑。 +- 在 Stalker transform 回调中犯错误最终抛出 JS 异常现在会导致 Stalker.unfollow(),因此错误不会被进程崩溃吞没。 +- 对 Stalker transform 调用 unfollow() 的强大支持。 +- Stalker 支持没有 AVX2 支持的旧 x86 CPU。 +- 支持禁用自动 Stalker 队列排空。 +- NativeFunction 通过全新的 Interceptor 展开 API 更好地处理异常。 +- Java 和 ObjC API 通过 clone(options) 为方法指定 NativeFunction 选项,并通过 ObjC.Block() 的第二个参数为块指定。 +- ObjC 类和协议缓存逻辑终于可以工作了。感谢 [@gebing][]! +- Windows 的预构建 Python 3 扩展终于支持 Windows 上所有 Python 3 版本 >= 3.4,就像在其他平台上一样。 +- ArrayBuffer wrap() 和 unwrap()。 +- DebugSymbol API 在 Linux/Android 上有更好的错误处理。 +- Java 集成不再在 Android 10 上的系统进程中的 recompileExceptionClearForArm64() 中崩溃。 +- i/macOS 上的 GumJS devkit 再次支持 V8。 + +### 12.8.1 中的变化 + +- CModule Stalker 集成恢复正常。 + +### 12.8.2 中的变化 + +- Thumb IT 块终于被正确重定位。这意味着我们能够在 32 位 ARM 目标(例如 Android)上 hook 更多函数。感谢 [@bigboysun][]! + +### 12.8.3 中的变化 + +- 引入 Java.ClassFactory.get() 以便能够使用多个类加载器而不必担心类名冲突。这意味着分配给 *loader* 属性现在被认为已弃用。我们仍然保留它以实现向后兼容性,但不支持将其与新 API 一起使用。 +- Java.enumerateLoadedClasses() 还提供类句柄,而不仅仅是名称。 +- JNI GetByteArrayRegion() 函数现在是 Env 包装器的一部分。感谢 [@iddoeldor][]! + +### 12.8.4 中的变化 + +- 当 PLT/GOT 条目尚未预热时,内部 hook 不再导致 Linux/ELF 目标上的崩溃。 + +### 12.8.5 中的变化 + +- Python 绑定终于在 Python 2.x 上提供正确编码的错误消息。 + +### 12.8.6 中的变化 + +- Android 链接器检测终于在沙盒进程中再次工作。这是 12.7.8 中引入的回归。感谢 [@DaveManouchehri][] 报告并帮助追踪这个问题! + +### 12.8.7 中的变化 + +- 我们的 Node.js *IOStream* 绑定收到了两个关键的稳定性改进。事实证明,取消逻辑有一个竞争条件,导致可取消对象并不总是被使用。拆卸逻辑中也有一个错误,可能导致在所有 I/O 操作完成之前关闭流。感谢 [@mrmacete][] 提供的这些很棒的修复! + +### 12.8.8 中的变化 + +- Gadget 在 Android/Linux 上的早期插桩用例中不再死锁,其中 Gadget 的入口点在持有动态链接器锁的情况下被调用。由于 Exceptor 现在使用 *dlsym()* 以避免在早期插桩期间遇到 PLT/GOT 问题,我们需要确保 Exceptor 从入口点线程初始化,而不是从 Gadget 线程初始化。 + +### 12.8.9 中的变化 + +- Stalker 的 JavaScript 集成不再在 *EventSink::stop()* 中执行 use-after-free,即在 *Stalker.unfollow()* 之后。 + +### 12.8.10 中的变化 + +- Gadget 再次能够在没有调试器的情况下在 iOS 上运行。这是 12.8.8 中引入的回归。感谢 [@ddzobov][] 报告! + +### 12.8.11 中的变化 + +- i/macOS Exceptor 的 API hook 在 Mach 异常处理 API 的用户仅请求处理程序的子集时不再执行 OOB 写入。这样的用户通常是崩溃报告器或分析框架。 +- 现在为 v8(稳定)和 v9(测试版)提供 Electron 预构建。我们不再为 v7 提供预构建。 + +### 12.8.12 中的变化 + +- 大规模改造 Android Java 集成,现在使用 Proxy 对象和 CModule 来延迟解析事物。不再使用 *eval* 来动态生成方法和字段包装器 —— 即每个生成的包装器所需的内存更少。所有这些更改都减少了内存使用量,并允许 *Java.use()* 更快地完成。 +- Android Java 集成提供对试图隐藏私有 API 的 Android 版本(即 Android >= 9)上的方法和字段的未经审查的访问。 +- 更快的 Android 设备枚举。当本地运行的 ADB 守护程序足够新(即 ADB >= 2017 年某个时候)时,不再运行任何 *adb shell* 命令来确定设备名称。 +- 我们终于消除了基于 Linux 的操作系统上的长期内存泄漏,影响受限进程,例如较新版本的 Android 上的 *zygote* 和 *system_server*。这是我们在给定线程退出后不久垃圾收集线程本地数据的逻辑中的一个错误。确定线程确实已完成退出的机制将失败,永远不会认为线程已消失。这将导致越来越多的垃圾积累,垃圾收集要迭代的垃圾越来越长。因此,我们不仅会在徒劳的 GC 尝试上花费越来越多的时间,而且还会每 50 毫秒重试一次 GC 来消耗 CPU。 +- Python 绑定允许从 Cancellable 获取文件描述符,以便将其集成到事件循环和其他 *poll()* 风格的用例中。值得注意的是,frida-tools 7.0.1 已发布,其中包含基于此的重大改进:CLI 工具在退出之前不再延迟最多 500 毫秒。因此,像 *frida-ls-devices* 和 *frida-ps* 这样的短期程序现在感觉非常快速。 +- Duktape 源映射处理现在也适用于 REPL 加载的脚本 —— 其中内联源映射不是脚本的最后一行,因为 REPL 附加了自己的代码。这意味着堆栈跟踪始终包含有意义的文件名和行号。 +- Duktape:内置的 JavaScript 运行时 —— 即 GumJS 的粘合代码、ObjC 和 Java —— 现在使用启用 *loose* 选项的 Babelified,以减少膨胀并提高性能。没有现代 JavaScript 数据结构通过 API 泄漏,因此不需要 Babel 符合规范。 +- V8:内置的 JavaScript 运行时被压缩,以获得更小的占用空间和更快的代码。这以前只对 Duktape 进行。 +- *enumerate_processes()* 中更好的 Linux 进程名称启发式。 + +### 12.8.13 中的变化 + +- *Java.performNow()* 恢复正常工作。 +- Python 绑定的 setup.py 现在在尝试下载之前查找本地 *.egg*,并期望下载在两分钟内完成。感谢 [@XieEDeHeiShou][] 提供的这些不错的改进! + +### 12.8.14 中的变化 + +- iOS 模拟器现在得到了正确支持,无论是 Gadget 形式还是从 macOS 附加到正在运行的模拟器进程。感谢 [@insitusec][] 帮助修复这些问题! +- Gadget 现在还在 iOS 上的上面目录中查找其 .config,但仅当其父目录名为"Frameworks"时。感谢 [@insitusec][] 的建议! + +### 12.8.15 中的变化 + +- 全新的功能完整的 iOS/arm64e 支持,包括新的 *NativePointer* 方法:*sign()*、*strip()*、*blend()*。 +- 现在支持最新的 iOS Unc0ver 越狱。感谢 [@mrmacete][] 的拉取请求,以及 [@Pwn20wnd][] 的协助!❤️ +- 改进了对 Chimera 越狱的支持,确保其 *pspawn_payload-stg2.dylib* 被初始化。感谢 [@mrmacete][]! +- i/macOS 注入器在 agent 入口点立即返回时不再失败。 +- 关于需要 Gadget 用于受限 iOS 的更好错误消息。 +- 改进了 Windows 注入器中的错误处理,以避免在我们的 DLL 注入失败时使目标进程崩溃。感谢 [@dasraf9][]! +- 支持在 i/macOS 上注入到活动的新生目标,并且不再将挂起的进程视为需要为注入做准备,无论它们是否真的需要。 +- 改进了 iOS 容错性,处理最前端 iOS 应用程序名称查询失败。 +- 改进了 Android 容错性,处理 *zygote* 和 *system_server* 进程死亡而无需重新启动 *frida-server*。 +- 现在能够在 Android 10 上的启动期间启动 *frida-server*,因为 *LD_LIBRARY_PATH* 不再干扰 *frida-helper-32* 的 spawn。感谢 [@enovella][] 帮助追踪这个问题! +- 在 UNIXy 平台上处理 *SIGABRT* 失败时不再无限循环。 +- 我们现在在 Exceptor 的 POSIX 后端中支持嵌套信号。感谢 [@bannsec][]! +- 正确处理无效的 Windows ANSI 字符串。感谢 [@clouds56][]! +- *Java.perform()* 在 Android < 5 上再次正常工作。 +- 改进了 *NativeFunction* 中的 varargs 处理,现在提升小于 int 的 varargs。感谢报告,[@0x410c][]! + +### 12.8.16 中的变化 + +- 大型 CModule 实例现在可以在具有 16K 页面的 iOS 系统上工作。感谢 [@mrmacete][] 发现并修复这个长期存在的问题! +- Stalker 也可以在 iOS/arm64e 上的 arm64 进程中工作。感谢 [@AeonLucid][] 报告并帮助追踪这个问题! + +### 12.8.17 中的变化 + +- 对 i/macOS 上的活动新生目标的注入支持导致了回归,因此我们暂时恢复了它。具体来说,iOS 12.4 上的 *notifyd* 是 *libSystemInitialized* 未设置的情况。需要更深入地挖掘以弄清楚为什么,所以决定暂时撤回这个逻辑。 + +### 12.8.18 中的变化 + +- 新的和改进的 *Java.scheduleOnMainThread()* 以允许调用诸如 *getApplicationContext()* 之类的 API。感谢 [@giantpune][] 报告! +- 能够在较新版本的 Android 上 hook CriticalNative 方法。感谢 [@abdawoud][] 报告! + +### 12.8.19 中的变化 + +- 如果在首次加载之前销毁脚本,现在可以正确清理脚本。这确保了最终当脚本核心被处置时,它反过来处置其对 Exceptor 的引用。如果不这样做,会导致在存在未加载脚本的情况下分离后附加时无限期挂起,因为 Exceptor 线程仍然留在目标进程中。感谢 [@mrmacete][] 发现并修复这个长期存在的问题! + +### 12.8.20 中的变化 + +- *remove_remote_device()* API 恢复正常工作。这是 12.7.17 中引入的不幸回归。感谢报告,[@CodeColorist][]! + + +[Stalker]: /docs/javascript-api/#stalker +[started]: /news/2017/08/25/frida-10-5-released/ +[AirSpy]: https://github.com/nowsecure/airspy +[frida-fuzz]: https://twitter.com/andreafioraldi/status/1205194910372110337 +[@andreafioraldi]: https://twitter.com/andreafioraldi +[frida-fs]: https://github.com/nowsecure/frida-fs +[@DaveManouchehri]: https://twitter.com/DaveManouchehri +[@CodeColorist]: https://twitter.com/CodeColorist +[@gebing]: https://github.com/gebing +[@bigboysun]: https://github.com/bigboysun +[@iddoeldor]: https://github.com/iddoeldor +[@mrmacete]: https://twitter.com/bezjaje +[@ddzobov]: https://github.com/ddzobov +[@XieEDeHeiShou]: https://github.com/XieEDeHeiShou +[@insitusec]: https://twitter.com/insitusec +[@Pwn20wnd]: https://twitter.com/Pwn20wnd +[@dasraf9]: https://github.com/dasraf9 +[@enovella]: https://twitter.com/enovella_ +[@bannsec]: https://twitter.com/bannsec +[@clouds56]: https://github.com/clouds56 +[@0x410c]: https://github.com/0x410c +[@AeonLucid]: https://twitter.com/AeonLucid +[@giantpune]: https://twitter.com/giantpune +[@abdawoud]: https://github.com/abdawoud diff --git a/_i18n/cn/_posts/2020-05-19-frida-12-9-released.markdown b/_i18n/cn/_posts/2020-05-19-frida-12-9-released.markdown new file mode 100644 index 00000000..19189489 --- /dev/null +++ b/_i18n/cn/_posts/2020-05-19-frida-12-9-released.markdown @@ -0,0 +1,142 @@ +--- +layout: news_item +title: 'Frida 12.9 发布' +date: 2020-05-19 22:00:00 +0200 +author: oleavr +version: 12.9 +categories: [release] +--- + +我们之前的重大发布都是关于 [Stalker][] 的。对于那些还不熟悉它的人来说,它基本上是一个代码跟踪引擎 —— 允许跟踪线程,捕获每个函数、每个块,甚至执行的每条指令。除了跟踪代码之外,它还允许您在任何地方添加和删除指令。它甚至使用高级 JIT 技巧来使所有这些变得非常快。 + +这听起来可能还有点抽象,所以让我们看几个例子。使用它的一种方法是当您想确定“[what other functions does this function call][]”时。或者,也许您想使用 Apple 的语音合成器在属于应用程序的代码中的每个 RET 指令处宣布 RAX 寄存器的值?[Here][] 是如何做到的。这是我早在 2017 年 [r2con presentation][] 上的演示之一。 + +直到现在,Stalker 仅在 Intel 架构和 ARM64 上可用。所以我很高兴地宣布 Stalker 现在也可以在 ARM32 上使用了!耶!🎉 我希望这个被严重怀念的 Stalker 后端能激励你们中的许多人开始在 Stalker 之上构建非常酷的东西。我觉得除了“仅仅”代码跟踪之外,它还有很大的潜力。结合 [CModule][],平衡快速原型设计和动态行为与性能变得非常容易。 + +在这个版本中有很多要谈的。其他主要变化之一是我们升级了所有依赖项。其中最有趣的可能是 V8,我们将它升级到了 8.4。这意味着您可以使用所有最新的 JavaScript 语言功能,例如 [optional chaining][] 和 [nullish coalescing operator][],而无需 [frida-compile][] 您的 agent。此外,还有性能改进,这是 V8 不断变得越来越好的另一个领域。 + +我们还刚刚添加了对 Android 11 Developer Preview 4 的支持,并且 iOS/arm64e 应用程序现在即使在受限 iOS 上也完全受支持。在我们所有支持的平台上的情况都有了很大改善。我想特别强调的一件事是,我们终于消除了一个影响我们基于 Duktape 的 JS 运行时的长期资源 [leak][] —— 这是一个自从我们使用 Duktape 作为默认 JS 运行时以来就一直存在的错误。 + +无论如何,真的没有简单的方法来深入挖掘所有改进的领域,所以一定要查看下面的变更日志。 + +享受吧! + + +### 12.9.0 中的变化 + +- Stalker 现在也可以在 ARM32 上使用。🎉 +- Stalker JS 集成不再破坏 *errno* / *LastError*。 +- *Stalker.follow()* 现在在 x86 和 ARM64 上是可靠的,包括当目标线程在 Windows 上处于系统调用中时。 +- Stalker 终于在 WoW64 上可靠了。感谢 [@zuypt][]! +- 所有依赖项已更新到最新最好的版本。最令人兴奋的是 V8 8.4,支持最新的 JavaScript 语言功能。 +- 长期存在的 Duktape 内存泄漏终于被发现并修复。感谢 [@disazoz][] 的错误报告导致了这一突破。 +- *Socket.connect()* 出错时不再泄漏文件描述符(及相关内存)。(通过 GLib 依赖项升级修复。)感谢报告,[@1215clf][]! +- *Kernel.read\*()* 不再在 V8 运行时中泄漏。 +- UNIX 构建系统迁移到 Meson 0.54。 +- Windows 构建系统迁移到 VS2019。 +- 除了 v10 和 v12 之外,还为 v14 提供了 Node.js 预构建。 +- 为 v8 和 v9 提供了 Electron 预构建。 +- F32 的 Fedora 软件包。 +- Ubuntu 20.04 的 Ubuntu 软件包。 +- Python 绑定不再使用任何已弃用的 API。 +- 支持仅 leanback 的 Android 应用程序。感谢 [@5murfette][]! +- iOS 受限 spawn() w/o closure 在 arm64e 上受支持。感谢 [@mrmacete][]! +- iOS usbmux 配对记录 plist 解析现在也处理二进制 plist,修复了一个长期存在的问题,即 Frida 会拒绝系留的 iOS USB 设备。感谢 [@pachoo][]! +- *ObjC.choose()* 也在 arm64e 上受支持。感谢 [@mrmacete][]! +- *ObjC.protocols* 枚举终于正常工作,而不仅仅是第一次。感谢报告,[@CodeColorist][]! +- 对 Android 11 Developer Preview 的初步支持。感谢 [@abdawoud][]! +- MUSL libc 兼容性。 +- 支持旧版本的 glibc,因此我们的二进制文件可以在各种桌面 Linux 系统上运行。 +- Libc shim 还涵盖 *memalign()* 并支持较新的 GNU 工具链。 +- Exceptor 的 POSIX 后端现在可以在 ARM32 上正确检测 Thumb,这以前会导致随机崩溃。 +- Exceptor 不再破坏 i/macOS 上的“rflags” (x86_64) 和“cpsr” (ARM64),并提供对本机上下文的写访问权限。 +- 向 libc shim 添加了四个基本的 i/macOS 64 位系统调用:*read()*, *write()*, *mmap()*, *munmap()*。感谢 [@mrmacete][]! +- 为了方便起见,iOS 二进制文件现在使用“skip-library-validation”权利进行签名。感谢 [@elvanderb][]! +- frida-core Vala API 绑定不再缺少 *frida.Error* 类型。 +- 我们的脚本现在允许在处于 *LOADING* 状态时向它们 *post()* 消息。这在脚本需要在 *load()* 期间发出同步请求时很有用。感谢 [@Gbps][]! +- Gadget 终于支持在 64 位 ELF 目标上使用 V8 运行时进行早期插桩,以前构造函数会以错误的顺序运行。感谢 [@tacesrever][]! +- 支持 *Device.open_channel()* 中除 TCP 之外的 ADB 通道。感谢 [@aemmitt-ns][]! +- *ArmWriter* 和 *ThumbWriter* API 中支持许多新指令。 +- 对我们的 ARM32 重定位器实现进行了大量改进。 +- 通过加载器调用时 Linux 模块枚举正常工作。 +- Linux 符号解析改进。 +- V8 运行时中更好的参数列表处理,将 *undefined* 视为与 Duktape 运行时相同。感谢 [@mrmacete][]! +- CModule Stalker API 恢复正常工作。 +- CModule 运行时现在公开 *Thread.{get,set}_system_error()*。 +- CModule 现在是 Linux/MIPS 上的存根,而不是由于 TinyCC 尚不支持 MIPS 而导致编译失败。 +- Capstone 配置为支持 ARMv8 A32 编码。 + +### 12.9.1 中的变化 + +- Python 绑定的 setup.py 对 macOS 上的 Python 3.x 执行正确的操作。 + +### 12.9.2 中的变化 + +- Fruity (iOS USB) 后端不再在 stdio 上发出警告。 + +### 12.9.3 中的变化 + +- 现在支持 Android 11 Developer Preview 4。感谢协助,[@enovella_][]! +- Linux 文件监控恢复良好状态。 +- ArmRelocator 正确重定位涉及 PC 寄存器的 ADD 指令。 +- ThumbRelocator 正确处理包含无条件分支的 IT 块。这意味着 Interceptor 能够 hook 更多棘手的情况。感谢 [@bet4it][]! +- Stalker ARM32 还支持 Thumb 模式下的 clone 系统调用。 +- Stalker ARM32 现在像 ARM64 后端一样抑制独占操作周围的事件。 +- Stalker ARM32 信任阈值支持。 +- 改进 ObjC 和 Java 桥接中的错误处理,以避免在不受支持的操作系统上使进程崩溃。 + +### 12.9.4 中的变化 + +- *ObjC.available* 不再假装 Objective-C 运行时可用,而实际上不可用。12.9.3 中的错误处理重构破坏了这一点,并且由于这是我们测试覆盖率的盲点,因此回归未被注意到。 +- Electron v9 已经发布,所以我们现在只提供 v9 的预构建。 + +### 12.9.5 中的变化 + +- iOS 早期插桩 —— 即 spawn() —— 在最新的 unc0ver 上受支持。 +- iOS 崩溃报告器集成移植到 iOS 13.5。 +- *SystemFunction* 现在在 Duktape 运行时中实现 *call()* 和 *apply()*,而不仅仅是在 V8 运行时中。 +- Java 桥接终于处理带有嵌入 nul 的字符串,修复了一个与 Java 桥接存在一样久的长期问题。感谢 [@tacesrever][]! + +### 12.9.6 中的变化 + +- 这次除了正确的 Windows 二进制文件外没有其他更改。Windows CI worker 上次实际上没有构建任何东西,并发布了陈旧的二进制文件。 + +### 12.9.7 中的变化 + +- iOS 早期插桩在 unc0ver 越狱上更可靠:我们现在作为早期插桩的一部分加载 *substrate-inserter.dylib*。这意味着它有机会引导进程,并让您 hook 引导程序 hook 的系统 API,而不必担心引导程序在遇到您的 hook 时会感到困惑。感谢 [@mrmacete][]! + +### 12.9.8 中的变化 + +- ApiResolver 实现现在通过将“/i”附加到查询字符串来支持不区分大小写的匹配。感谢 [@Hexploitable][]! +- *module* ApiResolver 不再泄漏 *MatchInfo* 实例。 +- CModule 运行时获得了 *GLib.PatternSpec* 和 GLib UTF-8 大小写助手。 +- 引入 *DebugSymbol.load()* 以便能够显式加载调试符号。目前这仅在 Windows 上实现,我们现在还支持“module!symbol”表示法以提高性能和精度。感谢 [@ohjeongwook][]! +- Java 桥接获得了一个全新的 API:*Java.enumerateMethods(query)* 这使得能够有效地定位与给定查询匹配的方法。 +- *ObjC.choose()* 不再因仅在我们的 V8 运行时中可重现的生命周期问题而崩溃。 + + +[Stalker]: /docs/stalker/ +[what other functions does this function call]: https://codeshare.frida.re/@oleavr/who-does-it-call/ +[Here]: https://github.com/frida/frida-presentations/blob/master/R2Con2017/02-transforms/06-return-values.js +[r2con presentation]: https://youtu.be/sBcLPLtqGYU +[CModule]: /docs/javascript-api/#cmodule +[optional chaining]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining +[nullish coalescing operator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator +[frida-compile]: https://github.com/oleavr/frida-agent-example +[leak]: https://github.com/svaarala/duktape/pull/2282 +[@zuypt]: https://github.com/zuypt +[@disazoz]: https://github.com/disazoz +[@1215clf]: https://github.com/1215clf +[@5murfette]: https://github.com/5murfette +[@mrmacete]: https://twitter.com/bezjaje +[@pachoo]: https://github.com/pachoo +[@CodeColorist]: https://twitter.com/CodeColorist +[@abdawoud]: https://github.com/abdawoud +[@elvanderb]: https://twitter.com/elvanderb +[@Gbps]: https://github.com/Gbps +[@tacesrever]: https://github.com/tacesrever +[@aemmitt-ns]: https://github.com/aemmitt-ns +[@enovella_]: https://twitter.com/enovella_ +[@bet4it]: https://github.com/bet4it +[@Hexploitable]: https://twitter.com/Hexploitable +[@ohjeongwook]: https://twitter.com/ohjeongwook diff --git a/_i18n/cn/_posts/2020-06-29-frida-12-10-released.markdown b/_i18n/cn/_posts/2020-06-29-frida-12-10-released.markdown new file mode 100644 index 00000000..976dabd7 --- /dev/null +++ b/_i18n/cn/_posts/2020-06-29-frida-12-10-released.markdown @@ -0,0 +1,193 @@ +--- +layout: news_item +title: 'Frida 12.10 发布' +date: 2020-06-29 22:00:00 +0200 +author: oleavr +version: 12.10 +categories: [release] +--- + +这次我们为 Java 开发人员和逆向工程师带来了一些令人兴奋的消息:[frida-java-bridge][] 现在支持 HotSpot JVM。这意味着我们的 Java 运行时桥接不再仅仅是 Android 的功能。非常感谢 [Razvan Sima][] 带来的这一惊人补充。 + +时机也再好不过了,因为我们最近还添加了 *Java.enumerateMethods(query)*,这是一个全新的 API,用于有效地定位与给定查询匹配的方法。我们确保也为 HotSpot JVM 实现了这一点。 + +查询指定为 `"class!method"`,允许使用通配符。它也可以后缀 `/` 和一个或多个修饰符: + +- `i`: 不区分大小写的匹配。 +- `s`: 包括方法签名,例如 `"putInt"` 变为 `"putInt(java.lang.String, int): void"`。方便匹配参数和返回类型,例如 `"*!*: boolean/s"` 匹配所有返回布尔值的方法。 +- `u`: 仅用户定义的类,忽略系统类。 + +例如: + +{% highlight js %} +Java.perform(() => { + const groups = Java.enumerateMethods('*youtube*!on*') + console.log(JSON.stringify(groups, null, 2)); +}); +{% endhighlight %} + +这可能会输出类似的内容: + +{% highlight json %} +[ + { + "loader": "", + "classes": [ + { + "name": "com.google.android.apps.youtube.app.watch.nextgenwatch.ui.NextGenWatchLayout", + "methods": [ + "onAttachedToWindow", + "onDetachedFromWindow", + "onFinishInflate", + "onInterceptTouchEvent", + "onLayout", + "onMeasure", + "onSizeChanged", + "onTouchEvent", + "onViewRemoved" + ] + }, + { + "name": "com.google.android.apps.youtube.app.search.suggest.YouTubeSuggestionProvider", + "methods": [ + "onCreate" + ] + }, + { + "name": "com.google.android.libraries.youtube.common.ui.YouTubeButton", + "methods": [ + "onInitializeAccessibilityNodeInfo" + ] + }, + … + ] + } +] +{% endhighlight %} + +我们还增强了 frida-trace 以支持 Java 方法跟踪: + +{% highlight bash %} +$ frida-trace \ + -U \ + -f com.google.android.youtube \ + --runtime=v8 \ + -j '*!*certificate*/isu' +Instrumenting... +X509Util.addTestRootCertificate: Auto-generated handler at "/Users/oleavr/__handlers__/org.chromium.net.X509Util/addTestRootCertificate.js" +X509Util.clearTestRootCertificates: Auto-generated handler at "/Users/oleavr/__handlers__/org.chromium.net.X509Util/clearTestRootCertificates.js" +X509Util.createCertificateFromBytes: Auto-generated handler at "/Users/oleavr/__handlers__/org.chromium.net.X509Util/createCertificateFromBytes.js" +X509Util.isKnownRoot: Auto-generated handler at "/Users/oleavr/__handlers__/org.chromium.net.X509Util/isKnownRoot.js" +X509Util.verifyKeyUsage: Auto-generated handler at "/Users/oleavr/__handlers__/org.chromium.net.X509Util/verifyKeyUsage.js" +X509Util.verifyServerCertificates: Auto-generated handler at "/Users/oleavr/__handlers__/org.chromium.net.X509Util/verifyServerCertificates.js" +ResourceLoader$CppProxy.native_enableDevCertificate: Auto-generated handler at "/Users/oleavr/__handlers__/com.google.android.libraries.elements.interfaces.ResourceLoader_CppProxy/native_enableDevCertificate.js" +ResourceLoader$CppProxy.enableDevCertificate: Auto-generated handler at "/Users/oleavr/__handlers__/com.google.android.libraries.elements.interfaces.ResourceLoader_CppProxy/enableDevCertificate.js" +AndroidCertVerifyResult.getCertificateChainEncoded: Auto-generated handler at "/Users/oleavr/__handlers__/org.chromium.net.AndroidCertVerifyResult/getCertificateChainEncoded.js" +bjbm.a: Auto-generated handler at "/Users/oleavr/__handlers__/bjbm/a.js" +bjbn.a: Auto-generated handler at "/Users/oleavr/__handlers__/bjbn/a.js" +AndroidNetworkLibrary.addTestRootCertificate: Auto-generated handler at "/Users/oleavr/__handlers__/org.chromium.net.AndroidNetworkLibrary/addTestRootCertificate.js" +AndroidNetworkLibrary.clearTestRootCertificates: Auto-generated handler at "/Users/oleavr/__handlers__/org.chromium.net.AndroidNetworkLibrary/clearTestRootCertificates.js" +AndroidNetworkLibrary.verifyServerCertificates: Auto-generated handler at "/Users/oleavr/__handlers__/org.chromium.net.AndroidNetworkLibrary/verifyServerCertificates.js" +vxr.checkClientTrusted: Auto-generated handler at "/Users/oleavr/__handlers__/vxr/checkClientTrusted.js" +vxr.checkServerTrusted: Auto-generated handler at "/Users/oleavr/__handlers__/vxr/checkServerTrusted.js" +vxr.getAcceptedIssuers: Auto-generated handler at "/Users/oleavr/__handlers__/vxr/getAcceptedIssuers.js" +ResourceLoader.enableDevCertificate: Auto-generated handler at "/Users/oleavr/__handlers__/com.google.android.libraries.elements.interfaces.ResourceLoader/enableDevCertificate.js" +Started tracing 18 functions. Press Ctrl+C to stop. + /* TID 0x339d */ + 955 ms AndroidNetworkLibrary.verifyServerCertificates([[48,-126,9,…],[48,-126,4,…]], "RSA", "suggestqueries.google.com") + 972 ms AndroidCertVerifyResult.getCertificateChainEncoded() + 1043 ms AndroidNetworkLibrary.verifyServerCertificates([[48,-126,4,…],[48,-126,4,…]], "RSA", "www.googleadservices.com") + 1059 ms AndroidCertVerifyResult.getCertificateChainEncoded() + /* TID 0x33a0 */ + 1643 ms AndroidNetworkLibrary.verifyServerCertificates([[48,-126,5,…],[48,-126,4,…]], "RSA", "googleads.g.doubleclick.net") + /* TID 0x339d */ + 1651 ms AndroidNetworkLibrary.verifyServerCertificates([[48,-126,9,…],[48,-126,4,…]], "RSA", "www.youtube.com") + /* TID 0x33a1 */ + 1665 ms AndroidNetworkLibrary.verifyServerCertificates([[48,-126,15,…],[48,-126,4,…]], "RSA", "lh3.googleusercontent.com") + /* TID 0x33a0 */ + 1674 ms AndroidCertVerifyResult.getCertificateChainEncoded() + /* TID 0x339d */ + 1674 ms AndroidCertVerifyResult.getCertificateChainEncoded() + /* TID 0x3417 */ + 1674 ms AndroidNetworkLibrary.verifyServerCertificates([[48,-126,15,…],[48,-126,4,…]], "RSA", "yt3.ggpht.com") + /* TID 0x33a1 */ + 1684 ms AndroidCertVerifyResult.getCertificateChainEncoded() + /* TID 0x3417 */ + 1688 ms AndroidCertVerifyResult.getCertificateChainEncoded() + 2513 ms AndroidNetworkLibrary.verifyServerCertificates([[48,-126,9,…],[48,-126,4,…]], "RSA", "redirector.googlevideo.com") + 2527 ms AndroidCertVerifyResult.getCertificateChainEncoded() + 2722 ms AndroidNetworkLibrary.verifyServerCertificates([[48,-126,9,…],[48,-126,4,…]], "RSA", "r1---sn-bxuovgf5t-vnaz.googlevideo.com") + /* TID 0x33a1 */ + 2741 ms AndroidNetworkLibrary.verifyServerCertificates([[48,-126,9,…],[48,-126,4,…]], "RSA", "r2---sn-bxuovgf5t-vnas.googlevideo.com") + /* TID 0x339d */ + 2758 ms AndroidNetworkLibrary.verifyServerCertificates([[48,-126,9,…],[48,-126,4,…]], "RSA", "r2---sn-bxuovgf5t-vnaz.googlevideo.com") + /* TID 0x33a1 */ + 2771 ms AndroidCertVerifyResult.getCertificateChainEncoded() + /* TID 0x3417 */ + 2772 ms AndroidCertVerifyResult.getCertificateChainEncoded() + /* TID 0x339d */ + 2777 ms AndroidCertVerifyResult.getCertificateChainEncoded() + 2892 ms AndroidNetworkLibrary.verifyServerCertificates([[48,-126,6,…],[48,-126,4,…]], "RSA", "r2---sn-bxuovgf5t-vnas.googlevideo.com") + /* TID 0x3417 */ + 2908 ms AndroidNetworkLibrary.verifyServerCertificates([[48,-126,6,…],[48,-126,4,…]], "RSA", "r2---sn-bxuovgf5t-vnaz.googlevideo.com") + /* TID 0x33a1 */ + 2926 ms AndroidNetworkLibrary.verifyServerCertificates([[48,-126,6,…],[48,-126,4,…]], "RSA", "r1---sn-bxuovgf5t-vnaz.googlevideo.com") + /* TID 0x3417 */ + 2935 ms AndroidCertVerifyResult.getCertificateChainEncoded() + /* TID 0x339d */ + 2937 ms AndroidCertVerifyResult.getCertificateChainEncoded() + /* TID 0x33a1 */ + 2942 ms AndroidCertVerifyResult.getCertificateChainEncoded() +{% endhighlight %} + +这刚刚作为 [frida-tools][] 8.0 的一部分发布 —— 您可以通过例如 `pip3 install -U frida-tools` 获取。 + +我们还在努力全面提高质量。一个很好的例子是 32 位 ARM 的 Stalker,它现在在 Android 上工作得更好。它也快得多,部分原因是导致 Thumb 块一遍又一遍地重新编译的错误。我们还实现了其他 Stalker 后端利用的自适应优化之一,仅此一项通常就相当于 ~5 倍的性能提升。 + +所以这应该涵盖了亮点 —— 但如果您对细节感到好奇,我强烈建议阅读下面的变更日志。 + +享受吧! + + +### 12.10.0 中的变化 + +- Java: 添加对 HotSpot JVM 的支持。使用 JVMTI 枚举类和选择对象。如果 JVM 库具有符号(macOS 上的 JDK 默认具有),则方法拦截有效。在 macOS 上使用 java 8, 11, 13, 14 进行了测试。感谢 [@0xraaz][]! +- Java: 修复 *_getUsedClass()* 不返回的问题,其中在不使用 *Java.perform()* 的情况下调用 *Java.use()* 两次会导致 *_getUsedClass()* 陷入无限睡眠循环。感谢 [@0xraaz][]! +- Java: 修复 *$alloc()*,它被前段时间的重构破坏了。 +- ObjC: 添加 *Block.declare()* 以便能够处理没有签名元数据的块。 +- ObjC: 修复 12.9.8 中引入的 ObjC *pointer* 处理回归。 + +### 12.10.1 中的变化 + +- Java: 允许 ClassFactory.get(null),为了在使用 enumerateMethods() 时方便。 +- Java: 恢复 JVM 方法调整逻辑,该逻辑意外地从拉取请求中删除。感谢 [@0xraaz][]! + +### 12.10.2 中的变化 + +- 修复 i/macOS 上长符号名称的处理。感谢 [@mrmacete][]! +- Java: 修复静态/最终方法的 JVM 拦截问题。感谢 [@0xraaz][]! +- 修复 Stalker ARM 对 Thumb-2 “mov pc, \” 的处理。 +- 修复 Stalker ARM 对易失性 VFP 寄存器的处理。 + +### 12.10.3 中的变化 + +- 修复 Fruity 后端中的设备移除接线。感谢 [@mrmacete][]! +- 避免在 *ArmWriter.put_branch_address()* 中破坏 R9。 +- 添加 *ThumbWriter.can_branch_directly_between()*。 +- 添加 *ThumbWriter.put_branch_address()*。 +- 改进 ThumbRelocator 以处理 ADR。 +- 修复 Stalker ARM 块损坏。 +- 修复 Thumb 块的 Stalker ARM 块回收逻辑。 +- 添加缺失的 Stalker ARM 延续逻辑,以支持长基本块。 +- 实现 Stalker ARM 回补逻辑以提高性能,通常为 5 倍。 + +### 12.10.4 中的变化 + +- 修复 V8 运行时中 *Module.name* 的编码。感谢 [@mrmacete][]! + + +[frida-java-bridge]: https://github.com/frida/frida-java-bridge +[Razvan Sima]: https://twitter.com/0xraaz +[frida-tools]: https://github.com/frida/frida-tools +[@0xraaz]: https://twitter.com/0xraaz +[@mrmacete]: https://twitter.com/bezjaje diff --git a/_i18n/cn/_posts/2020-07-24-frida-12-11-released.markdown b/_i18n/cn/_posts/2020-07-24-frida-12-11-released.markdown new file mode 100644 index 00000000..ddee64ea --- /dev/null +++ b/_i18n/cn/_posts/2020-07-24-frida-12-11-released.markdown @@ -0,0 +1,183 @@ +--- +layout: news_item +title: 'Frida 12.11 发布' +date: 2020-07-24 19:00:00 +0200 +author: oleavr +version: 12.11 +categories: [release] +--- + +为了期待 Apple 发布 macOS 11,Frida 12.11 来了!此版本带来了与 macOS 11 Beta 3 的完全兼容性。不仅如此,我们现在还支持 Apple silicon 上的 macOS。耶! + +值得注意的是,我们并没有止步于 arm64,我们还支持 arm64e。这个 ABI 仍然是一个移动的目标,所以如果你有一个 Developer Transition Kit (DTK) 并想试用一下,你必须禁用 SIP,然后添加一个引导参数: + +{% highlight bash %} +$ sudo nvram boot-args="-arm64e_preview_abi" +{% endhighlight %} + +考虑到这种令人敬畏的平台融合,实际上我们可能已经支持越狱的 iOS 14。一旦公开越狱可用,我们就会知道。至少它应该不需要太多工作来支持。 + +因此,对于那些探索 DTK 的人,您可以像往常一样获取我们的 CLI 工具和 Python 绑定: + +{% highlight bash %} +$ pip3 install frida-tools +{% endhighlight %} + +顺便说一句,我们刚刚发布了 [CryptoShark 0.2.0][],强烈建议您查看一下。唯一的警告是,我们目前仅提供 macOS/x86_64 的二进制文件,因此如果您想在 macOS/arm64 上尝试此操作,多亏了 Rosetta,您将能够运行它,但是附加到“本地系统”设备上的进程将不起作用。 + +不过解决方法很简单 —— 只需从我们的 [releases][] 中获取 frida-server 二进制文件并启动它,然后将 CryptoShark 指向“本地套接字”设备。如果您想在一个系统上运行 CryptoShark 并附加到另一个系统上的进程,您也可以使用本地 SSH 端口转发: + +{% highlight bash %} +$ ssh -L 27042:127.0.0.1:27042 dtk +{% endhighlight %} + +此版本中还有许多其他令人兴奋的更改,因此请务必查看下面的变更日志。 + +享受吧! + + +### 12.11.0 中的变化 + +- 添加对 macOS 11 和 Apple silicon 的支持。 +- 在 Darwin 上守护 helper 进程。感谢 [@mrmacete][]! +- 在 Linux 上守护 helper 进程。 +- 修复使用 usbmuxd 时不可靠的 iOS 设备处理。感谢 [@mrmacete][]! +- 修复 i/macOS frida-helper 提前死亡时的无限等待。 +- 添加 Android spawn() “uid” 选项以指定用户 ID。感谢 [@sowdust][]! +- 添加对最新 checkra1n 越狱的支持。感谢 [@Hexploitable][] 的协助! +- 提高 Stalker ARM 稳定性。 +- 堵塞 Interceptor arm64 后端错误路径中的泄漏。 +- 修复没有 RWX 页面的系统上 memcpy() 附近的拦截。 +- 修复 Darwin/arm64e 上 CpuContext 指针的编码。 +- 始终剥离 Darwin/arm64 上的回溯项。 +- 修复大端系统上的 Linux 架构检测。 +- 修复 ARM BE8 上的 Capstone 字节序配置。 + +### 12.11.1 中的变化 + +- 处理使用不同 ptrauth 密钥的 i/macOS 目标。 +- 修复大端系统上的 Linux CPU 类型检测。 +- 修复 Linux/ARM-BE8 上的早期插桩。 +- 修复注入到阻塞在 SIGTTIN 或 SIGTTOU 上的 Linux 进程的问题。 + +### 12.11.2 中的变化 + +- 修复 macOS 11/x86_64 上的 Stalker thread_exit 探测。 +- 修复 macOS 11/x86_64 上的缓慢导出解析。 +- 修复 CModule 对 ARM 上 Capstone 头文件的支持。 +- 将 ArmWriter 添加到 ARM 的 CModule 运行时。 +- qml: 添加对指定要使用的脚本运行时的支持。 + +### 12.11.3 中的变化 + +- 修复 V8 运行时中 ModuleMap.values() 的原型。 +- qml: 将 DetachReason 枚举与当前 Frida API 同步。 +- qml: 修复 Device 生命周期逻辑。 + +### 12.11.4 中的变化 + +- 修复 macOS 11 beta 3 上的注入器。放弃对旧 beta 的支持。 +- 放弃因 macOS 11 beta 3 而变得多余的 helper hack。 +- 修复 i/macOS 内省模块的处理。 + +### 12.11.5 中的变化 + +- 修复 macOS 11 和 iOS 14 上使用 dyld 现代代码路径的进程的 i/macOS 早期插桩。 +- 通过使用 VMThread::execute() 安装新方法,使 JVM 方法拦截更安全,该方法会阻塞所有 Java 线程,并使拦截热方法更安全。感谢 [@0xraaz][]! +- 向 ARM Relocator 添加对 SUB 指令的支持。这意味着在 32 位 ARM 上使用 Interceptor 和 Stalker 时提高了可靠性。 +- qml: 通过添加缺失的包含来修复使用 GCC 的构建。 + +### 12.11.6 中的变化 + +- 将 iOS 受限注入器移植到新的 arm64e ABI。这意味着 iOS 14 beta 3 现在在受限模式下完全受支持,即使在 A12+ 设备上也是如此。 + +### 12.11.7 中的变化 + +- 改进 Linux 和 QNX 上的 libc 检测。感谢 [@demantz][]! +- 修复 libdwarf 后端中符号大小的检查。这意味着 Linux 上更可靠的调试符号解析。 +- 修复脆弱的 Android activity 启动逻辑。感谢 [@muhzii][]! +- 通过清除 *kAccFastInterpreterToInterpreterInvoke* 标志来提高 Android Java hook 的可靠性。感谢 [@deroko][]! +- 防止在 *$dispose()* 之后使用 Java 包装器,以使此类危险错误更容易检测。 +- 改进 frida-qml 构建系统并添加对独立使用的支持。 + +### 12.11.8 中的变化 + +- 在 Apple silicon 上添加对 macOS 11 beta 4 的支持。 + +### 12.11.9 中的变化 + +- 添加对带有 Xcode 12 开发者磁盘映像的受限 iOS 的支持。 + +### 12.11.10 中的变化 + +- node: 堵塞 IOStream 的 WriteOperation 中的泄漏。感谢 [@mrmacete][]! +- qml: 添加对列出应用程序的支持。 +- qml: 在每个模型上公开“count”属性。 +- 修复“add sb, pc, r4”的 ARM 重定位。 +- 修复“add ip, pc, #4, #12”的 ARM 重定位。 +- 当 Rn 在 reglist 中时修复 LDMIA 的 ARM writer 支持。 + +### 12.11.11 中的变化 + +- 在 Android R 上添加对不透明 JNI ID 的支持,以支持可调试的应用程序。感谢 [@muhzii][]! +- qml: 添加对 spawn 进程的支持。 +- qml: 修复在 Linux 上与 devkit 链接时缺失的库。 +- qml: 修复 Linux 上的静态链接。 +- qml: 优化启动以不等待 enumerate_devices()。 + +### 12.11.12 中的变化 + +- 在 i/macOS 上的早期插桩期间初始化 CoreFoundation。感谢 [@mrmacete][]! +- 在 Stalker 中支持 NULL EventSink。感谢 [@meme][]! +- node: 提供 v10 和 v11 的 Electron 预构建。下一个版本将放弃 v9 的预构建。 +- qml: 添加 post(QJsonArray)。 + +### 12.11.13 中的变化 + +- 修复 Android 11/arm64 上的 ART 内部探测。感谢 [@enovella_][]! +- 暂时为 V8 构建不带压缩的 GumJS 运行时,因为我们需要改进 frida-compile 以使用最新版本的 [terser][]。 + +### 12.11.14 中的变化 + +- 现在 frida-compile 已升级到最新版本的 [terser][],为 V8 构建带压缩的 GumJS 运行时。 + +### 12.11.15 中的变化 + +- 添加对 iOS 14.x 安全 DTX 的支持。感谢 [@mrmacete][]! +- 修复 Android 11 上的 Java.deoptimizeEverything()。感谢 [@Gh0u1L5][]! + +### 12.11.16 中的变化 + +- 修复 Arm64Relocator.can_relocate() 中的 arm64e 支持。感谢 [@mrmacete][]! +- 向 Stalker.follow() 添加“onEvent”选项。这允许在本机代码中同步处理事件 —— 通常使用 CModule 实现。当想要实现自定义过滤和/或排队逻辑以提高性能,或牺牲性能以换取可靠的事件传递时很有用。 +- 向 EventSink 公开 Stalker 的实时 CpuContext。这可以通过“onEvent”回调和 Gum C API 访问。 +- 向 CModule 运行时添加 Spinlock。 + +### 12.11.17 中的变化 + +- 在受限 iOS 上通过 LLDB 杀死,以尽可能避免通过 ProcessControl 杀死。事实证明,我们以前的行为使 debugserver 处于不良状态,导致被杀死的应用程序有时会显示为已在运行,从而导致后续 spawn() 尝试的早期插桩失败。感谢 [@mrmacete][]! +- 通过让 instrumentation 字段检测优雅地失败来修复旧 Android API 级别上的 Java bridge 初始化。反正我们在旧 API 级别上不需要它。 +- 稍微减少每个脚本的 Duktape 内存使用量。不需要保留脚本源代码字符串。 + +### 12.11.18 中的变化 + +- 在受限 iOS 上检测最前端应用程序时跳过应用程序扩展。有时应用程序扩展作为第一个匹配的进程返回,随后抛出“无法解析 bundle ID 的 bundle 路径”。感谢 [@mrmacete][]! +- 改进 x86/x86_64 的 Android ART 检测偏移量检测。感谢 [@Gh0u1L5][]! +- 修复 Android 7.1-8.1 上的 JDWP 初始化失败。感谢 [@Gh0u1L5][]! +- 修复 libdwarf 后端中的最近符号逻辑。 +- 堵塞基于 Duktape 的运行时的参数解析逻辑中的泄漏,其中如果解析后续参数之一时发生错误,任何收集的内存范围数组都会泄漏。 + + +[CryptoShark 0.2.0]: https://github.com/frida/cryptoshark/releases/tag/0.2.0 +[releases]: https://github.com/frida/frida/releases +[@mrmacete]: https://twitter.com/bezjaje +[@sowdust]: https://github.com/sowdust +[@Hexploitable]: https://twitter.com/Hexploitable +[@0xraaz]: https://twitter.com/0xraaz +[@demantz]: https://github.com/demantz +[@muhzii]: https://github.com/muhzii +[@deroko]: https://github.com/deroko +[@meme]: https://github.com/meme +[@enovella_]: https://twitter.com/enovella_ +[terser]: https://github.com/terser/terser +[@Gh0u1L5]: https://github.com/Gh0u1L5 diff --git a/_i18n/cn/_posts/2020-10-28-frida-14-0-released.markdown b/_i18n/cn/_posts/2020-10-28-frida-14-0-released.markdown new file mode 100644 index 00000000..f994d244 --- /dev/null +++ b/_i18n/cn/_posts/2020-10-28-frida-14-0-released.markdown @@ -0,0 +1,164 @@ +--- +layout: news_item +title: 'Frida 14.0 发布' +date: 2020-10-28 05:55:00 +0200 +author: oleavr +version: 14.0 +categories: [release] +--- + +这是一个重要的新版本,经历了数周的紧张编码,喝了太多的咖啡。但在我们深入研究之前,我们需要快速回顾一下过去。 + +多年来,我们基于 V8 的运行时一直为我们服务得很好。但最终我们需要支持 V8 不太适合的受限系统,所以我们引入了第二个运行时。 + +这效果很好,但我们留下了一些权衡: + +- 两个运行时之间的语言功能支持差异很大。我们试图通过将简约运行时设为默认来缓解这种情况,因为它随处可用,并且在功能方面是最小公分母。 +- 使用 [frida-compile][] 等工具将现代 JavaScript 编译为在两个运行时上运行的旧 JavaScript 时,需要牺牲性能。 +- 具有大量代码和数据浮动的非平凡 agent 清楚地表明,不仅 V8 很快 —— 这不足为奇 —— 而且它非常擅长打包对象以避免浪费宝贵的 RAM。为了进一步扩大两个运行时之间的差距,V8 可以按原样运行现代 JavaScript,不需要运行包含兼容性垫片以填充缺失的运行时位(如 *Map* 和 *Set*)的臃肿版本。 +- 示例代码和文档往往看起来很晦涩,以避免混淆可能尝试在默认运行时上运行现代代码的用户。 +- 垃圾收集器实现的差异可能会隐藏用户在一个运行时中的错误,而在另一个资源释放得更急切的运行时中立即爆炸。一个这样的例子是在外部代码仍在使用 NativeCallback 时未能保持其存活。 +- 糟糕的用户体验:所有这些对我们的用户来说都是一个非常令人沮丧和困惑的故事。 +- 新功能和改进需要实现两次。出于显而易见的原因,这对我作为维护者来说真的很痛苦。 + +快进到 2019 年,[QuickJS][] 引起了我的注意。不过,当时我真的很忙于其他事情,所以当我仔细观察它时,我注意到它支持 ES2020,并且对于解释器来说 [performs][] 令人印象深刻。 + +但是,当我开始考虑从头开始建立一个新的运行时,并且看到其他两个大约各为 ~25 KLOC 时,这感觉太让人不知所措了。 + +不过,我一直回到 QuickJS 网站,如饥似渴地阅读技术细节,甚至在某个时候开始更深入地阅读公共 API。 + +然后我注意到它不支持协作多线程使用,即多个线程步调一致地执行 JavaScript。这使得面前的工作山感觉更加令人生畏,但后来我记得我已经为 Duktape 贡献了对此的支持,而且并没有那么难。 + +最终我鼓起勇气。从 GumJS 广泛的 [test-suite][] 中挑选了一个超级简单的测试作为我的第一个挑战,然后继续复制粘贴现有的两个运行时中最年轻的一个的 [ScriptBackend][] 和 [Script][] 实现。首先重命名事物,然后存根所有模块(Interceptor, Stalker 等),只是想得到一个近乎空的“shell”来编译和运行。 + +此时我上钩了,停不下来。消耗了大量的咖啡,在我知道之前,我已经实现了核心位和第一个模块。然后是另一个,再一个。 + +在使用 QuickJS API 进行了相当多的工作,并在其内部跳来跳去以确保我理解引用计数规则等之后,突然真的很清楚需要什么来实现协作多线程 API,这将是使其成为真正的运行时而不仅仅是玩具所必需的。 + +我们需要能够做的是在调用 NativeFunction 时暂停 JS 执行。这是因为被调用的函数可能会阻塞等待另一个线程可能已经持有的锁,但另一个线程可能刚刚调用了一个 hook 函数并正在等待进入 JS 运行时。因此,如果我们在调用 NativeFunction 之前不释放 JS 锁,我们现在就会陷入死锁。 + +另一个用例是调用 *Thread.sleep()* 或其他一些阻塞 API,如果我们持有 JS 锁时这样做,会导致饥饿。 + +无论如何,[QuickJS multi-threading API][] 证明是直截了当的,所以从那里我继续前进,直到一切最终 [done][]!🎉 + +此时我真的很好奇这个全新运行时的性能,首先是进入和离开它的成本问题。 + +继续在 iPhone 6 上试用它,运行 GumJS [test][],该测试使用 Interceptor hook 一个几乎为空的 C 函数,提供一个空的 JS 回调,然后测量每次调用所花费的挂钟时间,因为它不断地一遍又一遍地调用 C 函数。 + +这个想法是模拟如果用户 hook 一个频繁调用的函数会发生什么,以了解基本开销。 + +这是我得到的: + +{% highlight bash %} +# QuickJS + ok 1 /GumJS/Script/Interceptor/Performance/interceptor_on_enter_performance#QJS + ok 2 /GumJS/Script/Interceptor/Performance/interceptor_on_leave_performance#QJS + ok 3 /GumJS/Script/Interceptor/Performance/interceptor_on_enter_and_leave_performance#QJS +# Duktape + ok 4 /GumJS/Script/Interceptor/Performance/interceptor_on_enter_performance#DUK + ok 5 /GumJS/Script/Interceptor/Performance/interceptor_on_leave_performance#DUK + ok 6 /GumJS/Script/Interceptor/Performance/interceptor_on_enter_and_leave_performance#DUK +# V8 + ok 7 /GumJS/Script/Interceptor/Performance/interceptor_on_enter_performance#V8 + ok 8 /GumJS/Script/Interceptor/Performance/interceptor_on_leave_performance#V8 + ok 9 /GumJS/Script/Interceptor/Performance/interceptor_on_enter_and_leave_performance#V8 +{% endhighlight %} + +哇,这看起来很有希望![baseline memory usage][] 怎么样,即运行时本身的一个实例消耗多少内存? + +![QJS Memory Baseline](/img/qjs-memory-baseline.png "QJS Memory Baseline") + +这是一个相当大的改进 —— 只有以前运行时的五分之一! + +我好奇的下一件事是使用我们的 REPL 时 Frida 内部堆的大致初始大小。这包括 frida-agent、JS 运行时和加载的 REPL agent 使用的所有内存: + +![QJS Memory REPL](/img/qjs-memory-repl.png "QJS Memory REPL") + +耶,释放了 1 MB 用于其他目的! + +因此,我希望您像我一样对这个新版本感到兴奋。我们已经用这个基于 QuickJS 构建的全新运行时替换了我们以前的默认运行时。 + +作为一个实验,我也决定在没有 V8 运行时的情况下构建我们的官方二进制文件。这意味着二进制文件比以往任何时候都小得多。 + +我确实意识到你们中的一些人可能有 V8 运行时必不可少的用例,所以我希望你们能试用新的 QuickJS 运行时,并让我知道它对你们的效果如何。如果对于您的特定用例来说这绝对是一场灾难,请不要担心,只需告诉我,我们会想办法解决。 + +如果您想在启用 V8 运行时的情况下自己构建 Frida,只需调整 [this line][] 即可。但是,如果您离不开它,请务必告诉我,以便我们可以决定以后是否需要继续支持此运行时。 + +此主要版本中唯一的另一个更改适用于 i/macOS,我们终于跟随 Apple 的举措放弃了对 32 位程序的支持。不过我们暂时会保留代码路径,但我们的官方二进制文件脂肪少得多,顶级构建系统也稍微苗条一些。例如 `make core-macos-thin` 现在只是 `make core-macos`。 + +这就是 Frida 本身的所有内容,但还有更多。我们也发布了 frida-tools 9.0,刚刚升级以随处使用现代 JavaScript 功能。这包括 frida-trace,其中生成的样板 hook 在一些语法升级后变得更具可读性。最后但并非最不重要的一点是,我们也发布了 frida-compile 10.0,其中 Babel 依赖项消失了,相应的命令行开关也消失了;它更快,也简单得多。 + +因此,希望您会喜欢这个新版本! + +### 14.0.0 中的变化 + +- 将默认运行时替换为基于 QuickJS 的全新 GumJS 运行时。 +- 默认禁用 V8。 +- 在 V8 上保留 Interceptor.attach() 中的回调对象。 +- 从全局访问 API 中删除“enumerate”陷阱。 + +### 14.0.1 中的变化 + +- QJS: 修复嵌套的全局访问请求。 +- qml: 更新到新的 frida-core API。 + +### 14.0.2 中的变化 + +- QJS: 在调用期间保持 NativeCallback 存活。 +- QJS: 加速 NativeCallback 构造逻辑。 +- QJS: 暂时禁用堆栈限制。 +- iOS: 将 iOS 崩溃报告器集成移植到 iOS 14。 +- iOS: 删除 32 位的打包逻辑。 +- Android: 为“system_server” agent 使用默认运行时。 +- 现代化内部 JavaScript agent。 + +### 14.0.3 中的变化 + +- 在 Windows 上也禁用 V8。 +- iOS: 改进打包脚本。 + +### 14.0.4 中的变化 + +- iOS: 修复由工具链升级引起的 arm64e 回归。 + +### 14.0.5 中的变化 + +- QJS: 修复 Interceptor 错误处理。 + +### 14.0.6 中的变化 + +- ObjC: 修复替换方法的生命周期,使它们不绑定到类包装器,并且在链接用例中也保持存活。感谢 [@Hexploitable][] 和 [@mrmacete][] 的协助! +- 修复当 act == oact 时 Exceptor sigaction() 注册失败。感谢 [@hluwa][]! +- 改进 Linux libc 检测。 +- 修复在 Linux 上枚举和修改线程时间歇性挂起。 +- 修复不一致的 PC vs CPSR Thumb 位处理。 +- 修复 Linux/armhf 和 Linux/arm64 上的构建回归。 +- 发布 Raspberry Pi 32 位和 64 位二进制文件。 + +### 14.0.7 中的变化 + +- 避免在执行 JS 代码时崩溃的场景中死锁,例如在调用带有 `exceptions: 'propagate'` 的 NativeFunction 时,或者在 GumJS 中存在错误的情况下。感谢 [@mrmacete][]! +- 修复 macOS/arm64 上的 CModule。 +- 发布 Raspberry Pi 32 位的 Python 和 Node.js 二进制文件。 +- 发布 Fedora 33 而不是 Fedora 32 的二进制文件。 +- 发布 Ubuntu 20.10 的二进制文件。 + +### 14.0.8 中的变化 + +- 通过在上传连接中添加一些双向通信来提高受限 iOS 上传的可靠性。这是为了防止在复杂的远程配置中的 gadget 上传期间启动 DoS 保护。感谢 [@mrmacete][]! + + +[frida-compile]: https://github.com/frida/frida-compile +[QuickJS]: https://bellard.org/quickjs/ +[performs]: https://bellard.org/quickjs/bench.html +[test-suite]: https://github.com/frida/frida-gum/blob/6873f1504e40ad1a8bbc51d469c95519a2076fb0/tests/gumjs/script.c +[ScriptBackend]: https://github.com/frida/frida-gum/blob/6873f1504e40ad1a8bbc51d469c95519a2076fb0/bindings/gumjs/gumscriptbackend.h +[Script]: https://github.com/frida/frida-gum/blob/6873f1504e40ad1a8bbc51d469c95519a2076fb0/bindings/gumjs/gumscript.h +[QuickJS multi-threading API]: https://github.com/frida/quickjs/commit/7ec1392b19bcf6ae2b109cda3e2133c5d6918a6c +[done]: https://github.com/frida/frida-gum/commit/c47c0711c72a87e729e3e59110b7f611ff392fe2 +[test]: https://github.com/frida/frida-gum/blob/6873f1504e40ad1a8bbc51d469c95519a2076fb0/tests/gumjs/script.c#L5089-L5137 +[baseline memory usage]: https://github.com/frida/frida-gum/blob/6873f1504e40ad1a8bbc51d469c95519a2076fb0/tests/gumjs/script.c#L7356-L7399 +[this line]: https://github.com/frida/frida/blob/b5aa3aa623c2d919e7fe7c34eee9ded31da8212e/config.mk#L22 +[@Hexploitable]: https://twitter.com/Hexploitable +[@mrmacete]: https://twitter.com/bezjaje +[@hluwa]: https://github.com/hluwa diff --git a/_i18n/cn/_posts/2020-12-01-frida-14-1-released.markdown b/_i18n/cn/_posts/2020-12-01-frida-14-1-released.markdown new file mode 100644 index 00000000..ac9a00f3 --- /dev/null +++ b/_i18n/cn/_posts/2020-12-01-frida-14-1-released.markdown @@ -0,0 +1,131 @@ +--- +layout: news_item +title: 'Frida 14.1 发布' +date: 2020-12-01 12:00:00 +0200 +author: oleavr +version: 14.1 +categories: [release] +--- + +这次有很多好东西!🎉 让我们深入了解一下。 + +## 依赖项 + +我们刚刚将所有依赖项升级到了最新最好的版本。这项工作的一部分包括翻新用于构建它们的构建系统位。 + +有了这些改进,我们最终将支持完全从源代码构建 Frida 的旧版本,这是一个长期存在的问题,引起了很多挫折。 + +现在调整我们的依赖项也变得容易得多,例如在调试问题时。假设您正在排查为什么 *Thread.backtrace()* 在 Android 上工作不正常,您可能想要摆弄 libunwind 的内部结构。现在构建一个特定的依赖项真的很容易: + +{% highlight bash %} +$ make -f Makefile.sdk.mk FRIDA_HOST=android-arm64 libunwind +{% endhighlight %} + +或者,如果您正在为本地系统构建它: + +{% highlight bash %} +$ make -f Makefile.sdk.mk libunwind +{% endhighlight %} + +但是您可能已经构建了 Frida,并希望在其使用的预构建 SDK 中换出 libunwind。要做到这一点,您现在可以执行: + +{% highlight bash %} +$ make -f Makefile.sdk.mk symlinks-libunwind +{% endhighlight %} + +然后,您可以继续更改“deps/libunwind”,并通过重新运行以下命令执行增量编译: + +{% highlight bash %} +$ make -f Makefile.sdk.mk libunwind +{% endhighlight %} + +## iOS + +我们现在支持 iOS 14.2。它其实已经可以工作了,但是我们的崩溃报告器集成会死锁 Apple 的崩溃报告器,这对于整体系统稳定性来说并不好。 + +## GumJS 支持 size_t 和 ssize_t + +感谢 [@mame82][],我们终于在 *NativeFunction* 等 API 中支持“size_t”和“ssize_t”。这意味着您的跨平台 agent 不再需要维护与这些对应的本机类型的映射。耶! + +## 系统 GLib 支持 + +[Gum][] 终于可以使用上游版本的 GLib 构建,我们现在支持生成 [GObject introspection][] 定义。这为未来完全自动生成的语言绑定铺平了道路。 + +感谢 [@meme][] 带来的这些很棒的改进! + +## Windows 进程内注入 + +我们的 Windows 后端终于支持进程内注入。我的意思是,在目标进程架构相同且不需要提升权限的最常见情况下,我们现在可以避免将“frida-helper-{32,64}.exe”写出到临时目录并在我们能够 *attach()* 到给定目标之前启动它。作为额外的奖励,这也减少了我们的启动时间。 + +这项改进背后的动机是修复一个长期存在的问题,即某些端点安全产品会阻止我们的注入器工作,因为我们的逻辑很容易在此类软件中触发误报。当我们确实需要 spawn 我们的 helper 时,我们显然仍然会遇到此类问题,但现在很有可能最常见的用例实际上可以工作。 + +## Stalker ARM 改进 + +对于那些在 32 位 ARM 上使用 Stalker 的人来说,它现在应该比以往任何时候都工作得更好。此版本中包含了大量的修复。 + +## 字节码和 frida-tools + +自 14.0 发布以来的一个认识是,QuickJS 的字节码格式比预期的要不稳定得多。因此,我建议不要使用“frida-compile -b”,除非您的应用程序设计为仅与一个确切版本的 Frida 一起使用。 + +由于我在发布上一版 frida-tools 时没有意识到这个陷阱,我选择将 frida-trace agent 预编译为字节码。在致力于发布 14.1 时意识到我的错误后,我撤销了这个错误并发布了新版本的 frida-tools。 + +因此,请确保在升级时也获取其最新版本: + +{% highlight bash %} +$ pip3 install -U frida-tools +{% endhighlight %} + +## EOF + +还有很多其他令人兴奋的更改,所以一定要查看下面的变更日志。 + +享受吧! + + +### 14.1.0 中的变化 + +- 所有依赖项升级到最新最好的版本。 +- 严重翻新的依赖项构建系统。展望未来,我们将最终支持完全从源代码构建 Frida 的旧版本。 +- 将 iOS 崩溃报告器集成移植到 iOS 14.2。 +- 修复与 iOS 设备对话时的错误传播。 +- 在 GumJS 中添加对“size_t”和“ssize_t”的支持。感谢 [@mame82][]! +- 支持链接系统 GLib 和 libffi。感谢 [@meme][]! +- 支持 GObject Introspection。感谢 [@meme][]! +- 改进 Windows 后端以支持进程内注入。这意味着我们可以避开常见的 AV 启发式方法,并在目标进程架构相同且不需要提升权限的最常见情况下加快速度。 +- 修复 Stalker ARM 对“ldr pc, [sp], #4”的处理。 +- 修复 Stalker ARM 对 IT 块中标志的破坏。 +- 修复 Stalker ARM 对 IT 块中 CMN/CMP/TST 的处理。 +- 修复 ThumbWriter 指令的抑制标志。 +- 修复 Stalker ARM 排除逻辑的可靠性。 +- 修复 ARM Stalker 在 Thumb 模式下 follow() 线程的问题。 +- 修复 Thumb 模式下的 ARM Stalker SVC 处理。 +- 修复 ThumbRelocator 对未对齐 ADR 的处理。 +- 将 Stalker ARM 移动到运行时 VFP 功能检测。 +- 拒绝没有任何回调的 Interceptor.attach()。 +- 改进 GumJS 错误消息格式。 +- 修复 V8 调试器集成中的饥饿问题。 +- 在 V8 上调用期间也保持 NativeCallback 存活。 + +### 14.1.1 中的变化 + +- 修复 Capstone 丢失的 CModule 回归。 +- 为 32 位 ARM 添加缺失的 CModule 内置函数。 +- 修复 Android/ARM64 上的 Thread.backtrace()。 + +### 14.1.2 中的变化 + +- 修复 Android/ARM 上的 Thread.backtrace()。 + +### 14.1.3 中的变化 + +- 修复 ObjC.choose()。14.1.0 中的 TinyCC 升级暴露了一个现有错误。 +- 默认重新启用 V8。事实证明,我们有用例比 QuickJS 更适合它,并且它的调试器功能也被严重怀念。 +- 向 frida-server 添加 --ignore-crashes/-C 以禁用本机崩溃报告器集成。适用于不希望集成的情况,或者在运行我们尚未完全支持的前沿操作系统版本时。(崩溃报告器集成目前仅在 iOS 和 Android 上可用。) +- 增强 devkits 以确保 Capstone API 在所有平台上都公开。 +- 改进 devkit 示例。 + + +[@mame82]: https://twitter.com/mame82 +[Gum]: https://github.com/frida/frida-gum +[GObject introspection]: https://gi.readthedocs.io/en/latest/ +[@meme]: https://github.com/meme diff --git a/_i18n/cn/_posts/2021-02-05-frida-14-2-released.markdown b/_i18n/cn/_posts/2021-02-05-frida-14-2-released.markdown new file mode 100644 index 00000000..3b0b8682 --- /dev/null +++ b/_i18n/cn/_posts/2021-02-05-frida-14-2-released.markdown @@ -0,0 +1,333 @@ +--- +layout: news_item +title: 'Frida 14.2 发布' +date: 2021-02-10 10:00:00 +0200 +author: oleavr +version: 14.2 +categories: [release] +--- + +有很多要谈的。让我们从一个重要的新功能开始: + +## Realms + +Frida 支持 Android 已经有一段时间了,但有一个特定的功能一直被请求(主要)是那些认为自己看到了错误的用户。对话通常是这样开始的:“我在硬件加速的 Android 模拟器 X 中使用 Frida,当我 attach 到这个进程 Y 时,Process.enumerateModules() 缺少 JNI 库 Z。但我可以在 Process.enumerateRanges() 和 /proc/$pid/maps 中看到它。这是怎么回事?” + +正如您可能已经猜到的那样,我们谈论的是 Android 的 NativeBridge,通常用于基于 Intel 的 Android 设备,以使它们能够运行仅支持 ARM 的应用程序 —— 即具有一个或多个仅为 ARM 构建的 JNI 组件的应用程序。 + +然而,在 Frida 上下文中,我们通常谈论的是运行为 x86 构建的 Android 系统的基于 VirtualBox 的模拟器。该系统随后附带由 libhoudini(专有 ARM 翻译器)提供支持的 NativeBridge 支持。 + +有很多这样的模拟器,例如 BlueStacks, LDPlayer, NoxPlayer 等。虽然提到的那些是为运行游戏而优化的,但现在也有 Google 官方的 Android 11 AVD,它们开箱即用地提供 NativeBridge 支持。 + +多年来,我一直在思考我们如何在 Frida 中支持这种情况,但思考它总是让我头疼。不过,这确实感觉像是我们应该在某个时候支持的东西,我只是很难弄清楚 API 会是什么样子。 + +然后到了 2020 年,Apple 宣布过渡到 ARM,突然 Rosetta 再次变得相关。“好吧”,我想,“现在我们有两个平台,在这些平台上支持包含运行遗留代码的模拟 realm 的进程将非常有用。” + +是的,还有 Windows,但我们还不支持 ARM 上的 Windows。不过我们完全应该支持,所以如果有人有兴趣尝试一下,请*务必*联系我们。 + +无论如何,我很高兴地宣布,我们用于 x86 和 x86_64 的 Android 二进制文件现在开箱即用地支持此类进程。您可能已经熟悉以下 frida-core API,其中 Python 风格如下所示: + +{% highlight python %} +session = device.attach(target) +{% endhighlight %} + +(或者 *frida.attach()*,如果您的代码仅处理本地系统。) + +如果 *target* 有一个模拟 realm,您现在可以这样做: + +{% highlight python %} +session = device.attach(target, realm='emulated') +{% endhighlight %} + +默认值为 `realm='native'`,实际上您可以同时使用两个 realm。当使用我们的 CLI 工具时,传递 `--realm=emulated` 以作用于模拟 realm。 + +在 Android 上使用此功能的一个重要警告是,您需要在 *native* realm 中应用 Java 级检测。 + +最后值得注意的是,这个新功能目前仅在 Android 上受支持,但以后支持 macOS 上的 Rosetta 应该不难。如果您想帮忙,请务必联系我们。 + +## 将 Android Java Hooking 内联 + +Frida 的 [Java bridge][] 在 Android 上替换 Java 方法的方式直到现在都是通过改变内存中的方法元数据来实现的,以便目标方法变为 native —— 如果它还不是的话。这允许我们安装一个与给定方法的 JNI 签名匹配的 NativeCallback。 + +这带来了一些挑战,因为 ART 运行时确实有其他依赖于给定方法个性的内部状态。我们设计了一些黑客手段来绕过其中一些问题,但一些特别棘手的边缘情况仍未解决。一个这样的例子是 ART VM 维护的 JIT 分析数据。 + +我一直在思考的一个想法是停止改变方法元数据,而是对 AOT 生成的机器代码执行内联 hook —— 对于非 native 方法而言。这仍然留下了在 VM 解释器上运行的方法,但假设我们可以通过 hook VM 内部来处理这些方法。 + +我尝试了一个早期原型来进一步探索这种方法。它似乎可行,但仍有许多挑战需要解决。在与 [@muhzii][] 进行了一些头脑风暴之后,他继续在业余时间进一步发展这个粗略的 PoC。然后有一天,当我看到他刚刚打开的惊人拉取请求时,我几乎兴奋得从椅子上摔下来。 + +感谢 Muhammed 的出色工作,您现在都可以在 Android 上享受大大改进的 Java 检测体验。这意味着提高了稳定性,也意味着直接调用不会绕过您的替换方法。耶! + +## 去优化 + +对于那些在 Android 上使用 Java.deoptimizeEverything() 以确保您的 hook 不会因优化而被跳过的人,现在有一个更细粒度的替代方案。感谢 [@alkalinesec][] 对我们 Java bridge 的巧妙贡献,您现在可以使用 Java.deoptimizeBootImage()。它确保只有引导映像 OAT 文件中的代码被去优化。在某些情况下,这是一个严重的性能提升,在这些情况下,应用程序代码本身在去优化时很慢,并且为了可靠地命中 hook,不需要去优化它。 + +## CModule + +这里还有另一个非常令人兴奋的更新。我们故事中的下一个英雄是 [@mephi42][],他开始将 Frida 移植到 S390x。我们的 CModule 实现幕后依赖于 TinyCC,它尚不支持此架构。系统可能有一个 C 编译器,所以 @mephi42 建议我们添加对在 TinyCC 无法帮助我们的系统上使用 GCC 的支持。 + +我真的很喜欢这个主意。不仅从架构支持的角度来看,而且还因为可能获得更快的代码 —— TinyCC 针对小编译器足迹和快速编译进行了优化,而不是快速代码。 + +所以不用说,随着每一个朝着 GCC 支持的拉取请求,我都变得越来越兴奋。一旦最后一个落地,它就启发了我添加对在 i/macOS 上使用 Apple clang 的支持。 + +最后我们得到了这个: + +{% highlight js %} +const cm = new CModule(`…`, {}, { toolchain: 'external' }); +{% endhighlight %} + +其中 `toolchain` 是 `any`, `internal`, 或 `external`。默认值为 `any`,这意味着如果 TinyCC 支持您的 `Process.arch`,我们将使用它,否则回退到 `external`。 + +不过,故事并没有到此结束。在实现对 i/macOS 的支持时,我并不清楚我们如何融合 JavaScript 端提供的符号。(CModule 构造函数的第二个参数。) + +GCC 实现使用链接器脚本,这是一个 Apple 链接器不支持的非常优雅的解决方案。但这击中了我:我们已经有了用于注入器的自己的动态链接器。 + +一旦我把它连接起来,似乎真的很明显,我们也可以简单地支持完全跳过 Clang,并允许用户传入预编译的共享库。 + +当时的想法是,这将启用交叉编译,但也使得用 Swift 和 Rust 等语言实现 CModule 成为可能:基本上任何可以与 C 互操作的东西。 + +所以这意味着我们现在也支持以下内容: + +{% highlight js %} +const cm = new CModule(blob); +{% endhighlight %} + +其中 `blob` 是包含要从中构造它的共享库的 ArrayBuffer。目前这部分仅在 i/macOS 上实现,但目标是在所有平台上支持它。(欢迎贡献!) + +此外,从 frida-tools 9.2 开始,REPL 的 `-C` 开关也支持这一点,使得使用外部工具链变得容易,而不会错过实时重新加载 —— 这使得开发期间的反馈循环更短。 + +更进一步,CModule API 现在还提供属性 `CModule.builtins`,脚手架工具可以使用它来获取内置头文件和预处理器定义。 + +关于这一点,我们现在在 frida-tools 中有这样一个工具: + +{% highlight sh %} +$ mkdir pewpew +$ cd pewpew +$ frida-create cmodule +Created ./meson.build +Created ./pewpew.c +Created ./.gitignore +Created ./include/glib.h +Created ./include/gum/gumstalker.h +Created ./include/gum/gumprocess.h +Created ./include/gum/gummetalarray.h +Created ./include/gum/guminterceptor.h +Created ./include/gum/gumspinlock.h +Created ./include/gum/gummetalhash.h +Created ./include/gum/gummemory.h +Created ./include/gum/gumdefs.h +Created ./include/gum/gummodulemap.h +Created ./include/json-glib/json-glib.h +Created ./include/gum/arch-x86/gumx86writer.h +Created ./include/capstone.h +Created ./include/x86.h +Created ./include/platform.h + +Run `meson build && ninja -C build` to build, then: +- Inject CModule using the REPL: frida Calculator -C ./build/pewpew.dylib +- Edit *.c, and build incrementally through `ninja -C build` +- REPL will live-reload whenever ./build/pewpew.dylib changes on disk + +$ meson build && ninja -C build +… +[2/2] Linking target pewpew.dylib +$ frida Calculator -C ./build/pewpew.dylib +… +init() +[Local::Calculator]-> +{% endhighlight %} + +是的,它实时重新加载!做到极致,您可以使用文件监视工具并在 `pewpew.c` 更改时让它运行 `ninja -C build` —— 然后只需保存并立即看到检测在目标进程中生效。 + +值得注意的是,当使用内部 CModule 工具链时,您也可以使用上述内容,因为磁盘上有可用的头文件对于代码完成等编辑器功能很方便。 + +## EOF + +还有很多其他令人兴奋的更改,所以一定要查看下面的变更日志。 + +享受吧! + + +### 14.2.0 中的变化 + +- 全新的 realms API,用于在 native 进程内检测模拟 realm。目前仅在 Android 上实现。 +- 添加 Java.deoptimizeBootImage()。感谢 [@alkalinesec][]! +- 向 frida-server 添加 --disable-preload/-P。在操作系统兼容性问题的情况下很有用,其中 Frida 在附加到某些操作系统进程时会导致它们崩溃。 +- 修复旧版本 Android 上的 libc 检测。 +- 修复在 Android 上解析 vDSO 导出时的崩溃。感谢 [@ant9000][]! +- 恢复 Android 上对 libhoudini 的支持。 +- 修复 Android 11 翻译器上的 ARM 缓存刷新。 +- 修复 Android 5.x 的链接器偏移量。感谢 [@muhzii][]! +- 开始重构 CModule 的内部结构以准备多个后端。感谢 [@mephi42][]! +- 修复 ARM 上的 CModule 聚合初始化。 +- 修复 ModuleApiResolver 快速路径发出错误匹配。 + +### 14.2.1 中的变化 + +- 修复 V8 运行时中的 CModule 构造函数错误路径。 +- 在 Android 上为“system_server” agent 使用 V8 运行时。 + +### 14.2.2 中的变化 + +- 修复 Darwin.Mapper arm64e 处理没有修复的页面。这纯粹出于“运气”未被注意到,直到我们的二进制文件最终发生足够的变化以暴露此错误。 + +### 14.2.3 中的变化 + +- 升级到使用内联 hook 用于 ART 运行时。感谢 [@muhzii][]! +- 修复 i/macOS 上的直接传输回归,这是由 GLib 升级引入的,其中 GLib.Socket 在 Apple 操作系统上获得了 GLib.Credentials 支持。此回归的典型症状是 frida-server 被 Jetsam 杀死。 +- 修复 32 位 Windows 上对 stdcall, thiscall, 和 fastcall 的 libffi 支持。 +- 扩展 Memory.alloc() 以支持在给定地址附近分配。感谢 [@muhzii][]! +- 修复 x86_64 上 RIP 相对间接分支的重定位。感谢 [@dkw72n][]! +- 通过在放弃之前咨询调试符号来改进 JVM C++ 分配器 API 探测逻辑。感谢 [@Happyholic1203][]! +- 升级 SELinux 库以支持前沿 Android 系统。 +- 添加 gum-linux-x86_64-gir 目标用于 GIR 生成。感谢 [@meme][]! + +### 14.2.4 中的变化 + +- 修复使用 ART 解释器时的 Android 性能回归,例如在使用 deoptimizeEverything() 或 deoptimizeBootImage() 时,这会导致我们的 JS 回调变得非常热。将热回调移动到 CModule 以加快速度。 +- 修复 Linux 上 Node.js 绑定中的 V8 调试器支持。 +- 修复 libdwarf 后端中 ELF init 错误时的崩溃。 + +### 14.2.5 中的变化 + +- 修复 14.2.4 中引入的旧 Android 系统上的回归。 + +### 14.2.6 中的变化 + +- 修复与旧版 NativeBridge v3 及更高版本的兼容性,其中需要指定命名空间。 + +### 14.2.7 中的变化 + +- 修复在 printf() 渲染 %p 时没有“0x”前缀的系统上的 frida-java-bridge 崩溃。 +- 修复 ARM64 上的 jni_ids_indirection_ 偏移量解析。感谢 [@muhzii][]! + +### 14.2.8 中的变化 + +- 修复 i/macOS 上的 GLib SO_NOSIGPIPE 回归。这通常会导致 frida-server 由于 SIGPIPE 而死亡。感谢 [@mrmacete][]! +- 重构 CModule 内部结构并为 GCC 后端奠定基础。感谢 [@mephi42][]! +- 为只关心事件且不需要生命周期 hook 或代码转换的 Stalker C API 消费者添加 EventSink.make_from_callback()。 +- 在块的开头发出 Stalker BLOCK 事件,因为这是最直观的,因为人们期望至少有与 COMPILE 事件一样多的 BLOCK 事件。此行为也最适合测量覆盖率。 +- 添加 Stalker 预取支持,对于优化“AFL fork server”类用例很有用。 + +### 14.2.9 中的变化 + +- 处理 Darwin CodeSegment 后端中的永久条目。从 iOS 14.3 开始,在 A12+ 设备上,当目标 VM 映射条目标记为“永久”时,mach_vm_remap() 可能会返回 KERN_NO_SPACE。感谢 [@mrmacete][]! +- 在 CModule 中连接 GCC 支持。感谢 [@mephi42][]! +- 为 Apple 操作系统上的 Clang 添加 CModule 后端。 +- 添加对链接预构建 CModule 的支持。(目前仅在 i/macOS 上。) +- 最终确定 CModule 工具链选择 API。 +- 添加 CModule.builtins 属性以支持工具。 +- 默认生成 frida-core GIR。感谢 [@meme][]! +- 修复 Linux/MIPS 上的回归。 + +### 14.2.10 中的变化 + +- 改进 frida-inject 以支持双向 stdio。 +- 在 frida-python 中添加对 Termux 的支持:`pip install frida-tools` 现在可以工作了。 + +### 14.2.11 中的变化 + +- 改进 frida-inject 以支持原始终端模式。 +- 为 Darwin 添加内部策略守护进程。 +- 改进 Gum.Darwin.Mapper 以支持严格内核。 + +### 14.2.12 中的变化 + +- 修复 GC 后 ART 方法 hook 的可靠性。感谢 [@muhzii][]! + +### 14.2.13 中的变化 + +- 修复 x86 上的 Instruction 操作数解析,确保立即数始终由 Int64 表示,而不是数字。感谢 [@muhzii][]! +- 修复进程未附加到终端时的 frida-inject。感谢 [@muhzii][]! +- 向 CModule 公开 Base64 和 Checksum GLib 原语。感谢 [@mrmacete][]! + +### 14.2.14 中的变化 + +- 修复早期加载时 i/macOS 上的 Gadget 崩溃。 +- 使 frida-inject stdin 通信可选。感谢 [@muhzii][]! +- 支持在 unc0ver 6.x 上 spawn iOS 应用程序。感谢 [@mrmacete][]! +- 解决 spawn iOS 应用程序时的单步延迟,以避免随机失败。感谢 [@mrmacete][]! +- 修复 libc shim 中的 read() 签名不匹配,这将导致在较新的 Apple 工具链上出现编译错误。感谢 [@Manouchehri][]! +- 修复较新版本 Android 上的 Android enumerate_applications() 名称截断。感谢 [@pancake][] 报告并帮助解决这个问题! +- 修复目标无法加载 frida-agent 时的挂起。 +- 修复对附加到 Windows 服务的支持。 +- 在注册新服务之前清理陈旧的 Windows 服务。 +- 添加构建选项以支持使用已安装的资产而不是嵌入它们。 +- 更新 iOS 打包以使用已安装的资产。 +- 添加对受限 Android 的基本支持。感谢 [@enovella_][] 在这方面所有有趣和富有成效的结对编程! +- 扩展 Arm64Writer API 以支持更多立即数。 +- 改进 Stalker 以支持 arm64 上暂时未对齐的堆栈。 +- 修复 Stalker follow() 在没有 sink 时的崩溃。 +- 实现 Stalker 失效支持。这允许更新检测而无需丢弃所有已翻译的代码。感谢 [@p1onk][] 在这方面的协助! +- 添加 Gum.DarwinModule.enumerate_function_starts()。 +- 添加 Gum.DarwinGrafter 用于 AOT 嫁接,以便能够在无法进行运行时代码修改时准备二进制文件进行检测。感谢 [@mrmacete][] 的协助! +- 添加 Memory.allocate_near()。 +- 提高所有支持架构上的 Stalker 性能和健壮性: + - 改进调用探测以在目标处而不是调用站点进行探测,并利用新的失效基础设施。 + - 处理从调用探测中添加/删除调用探测。 + - 重构 callout 处理,以便可以在失效时销毁用户数据。这也消除了 callout 锁。 + - 在自修改代码的情况下,重新编译而不是分配新块。 + - 为代码和数据使用单独的 slab,以避免由于元数据存储空间不足而仅使用部分 slab 的情况。 + - 在 ExecCtx 中内联第一个代码/数据 slab,因此我们在跟踪不接触太多代码或根本不接触代码(如果它们没有唤醒)的线程时使用的内存要少得多。 + - 当 trust_threshold 为 0 时,不要费心存储原始代码。 + - 简化 Stalker 块元数据以减少每个块的内存消耗。 +- 修复 ART 上 Java.enumerateMethods() 的结果。这是一个错误,其中静态初始化方法作为 '$init' 包含在枚举集中,而它们应该完全被跳过。感谢 [@muhzii][]! +- 修复 Java 方法 hook 期间使用的 Android/ART 近内存分配代码路径。感谢 [@muhzii][]! +- 修复泛型 Java 数组类型的处理。这允许稍后在编组数组类型时重用从运行时获得的数组对象,这对于保留类型信息是必要的,特别是在类型是动态的情况下。感谢 [@muhzii][]! +- 将 Android/ART StackVisitor 移植到 x86, x64, 和 ARM32。感谢 [@P-Sc][]! +- 修复 Android 上的 ARM 缓存刷新。事实证明 cacheflush() 在 Linux/ARM 上期望一个范围。这个 32 位 Android/ARM 回归是在 14.2.0 中引入的。 +- 为 32 位 ARM 添加一些缺失的 TinyCC 内置函数。感谢 [@giantpune][] 报告并帮助解决这个问题! +- 修复 Stalker 块回收逻辑中的回绕。 +- 修复从大数字构造 V8 NativePointer。 +- 修复 Windows 上的 Stalker 本地线程操作。 +- 修复 CModule 临时目录清理逻辑。 +- 删除被遗忘的 InspectorServer 调试代码。 +- 修复 V8 调试器集成。感谢 [@taviso][] 报告! + +### 14.2.15 中的变化 + +- 修复与最新 unc0ver iOS 越狱的兼容性。感谢 [@mrmacete][]! +- 添加对 Anbox 的支持。感谢 [@asabil][]! +- 添加 Java.deoptimizeMethod()。感谢 [@liuyufei][]! +- 处理替换可能被去虚拟化的 ART 方法。感谢 [@liuyufei][]! + +### 14.2.16 中的变化 + +- 为 32 位 ARM 添加许多缺失的 TinyCC 内置函数。感谢 [@giantpune][] 报告并帮助解决这个问题! +- 修复在 arm64 上使用 ADRP 时的 Android ART trampoline 对齐,以前在尝试替换某些方法时会导致抛出 `Error: invalid argument` 异常。感谢 [@pandasauce][] 报告并帮助解决这个问题! + +### 14.2.17 中的变化 + +- 枚举来自链式修复的 Darwin 导入,以支持最新的 arm64e 二进制文件。感谢 [@mrmacete][]! +- 修复受限 iOS 注入器中的链式修复处理。感谢 [@mrmacete][]! +- qml: 使用 *no_keywords* 编译以实现 GLib 兼容性。感谢 [@suy][]! + +### 14.2.18 中的变化 + +- 修复最近 XNU 版本上的 i/macOS 注入器,其中 mach_port_extract_right() 在尝试窃取目标进程的 POSIX 线程端口发送权限时失败并显示 KERN_INVALID_CAPABILITY。这导致注入器假设我们已经取消注入,随后释放仍在使用的内存。 +- 修复受限 iOS 注入器中的 \_\_\_error 符号名称。感谢 [@mrmacete][]! +- 修复 Linux 后端中路径上带有空格的模块枚举。感谢 [@suy][]! +- 修复 x64 上直接分支地址的 Stalker 处理。 +- python: 添加 RPC 导出列表功能。感谢 [@NewbieGoose][]! + + +[@alkalinesec]: https://twitter.com/alkalinesec +[Java bridge]: https://github.com/frida/frida-java-bridge +[@muhzii]: https://github.com/muhzii +[@mephi42]: https://github.com/mephi42 +[@ant9000]: https://github.com/ant9000 +[@dkw72n]: https://github.com/dkw72n +[@Happyholic1203]: https://github.com/Happyholic1203 +[@meme]: https://github.com/meme +[@mrmacete]: https://twitter.com/bezjaje +[@Manouchehri]: https://github.com/Manouchehri +[@pancake]: https://twitter.com/trufae +[@enovella_]: https://twitter.com/enovella_ +[@p1onk]: https://twitter.com/p1onk +[@P-Sc]: https://github.com/P-Sc +[@giantpune]: https://twitter.com/giantpune +[@taviso]: https://twitter.com/taviso +[@asabil]: https://twitter.com/asabil +[@liuyufei]: https://github.com/liuyufei +[@pandasauce]: https://github.com/pandasauce +[@suy]: https://github.com/suy +[@NewbieGoose]: https://github.com/NewbieGoose diff --git a/_i18n/cn/_posts/2021-07-18-frida-15-0-released.markdown b/_i18n/cn/_posts/2021-07-18-frida-15-0-released.markdown new file mode 100644 index 00000000..0f093213 --- /dev/null +++ b/_i18n/cn/_posts/2021-07-18-frida-15-0-released.markdown @@ -0,0 +1,586 @@ +--- +layout: news_item +title: 'Frida 15.0 发布' +date: 2021-07-18 00:48:05 +0200 +author: oleavr +version: 15.0 +categories: [release] +redirect_from: /news/2021/06/16/frida-15-0-released/index.html +--- + +发生了太多的变化。让我们从指导此版本中大多数其他更改的重大新功能开始: + +## Portals + +### 第一部分:构想 + +今年早些时候,[@insitusec][] 和我在集思广益,探讨如何简化分布式检测用例。本质上是发布一个“空心”的 Frida Gadget,其中特定于应用程序的检测由后端提供。 + +实现此目的的一种方法是使用 Socket.connect() JavaScript API,然后定义特定于应用程序的信令协议,通过该协议加载代码,然后再将其移交给 JavaScript 运行时。 + +但是这种做事方式很快就会导致相当多的无聊胶水代码,并且像 frida-trace 这样的现有工具实际上无法在这样的设置中使用。 + +就在那时,@insitusec 建议也许 Frida 的 Gadget 可以提供与其 [Listen][] 交互相反的对应物。因此,它不再是公开 frida-server 兼容接口的服务器,而是可以配置为充当连接到 Portal 的客户端。 + +然后,这样的 Portal 聚合所有连接的 Gadget,并且还公开了一个 frida-server 兼容接口,所有这些 Gadget 都作为进程出现在其中。在外界看来,它们就像是 Portal 运行所在的同一台机器上的进程:如果您使用 enumerate_processes() 或 frida-ps,它们都有唯一的进程 ID,并且可以无缝地 attach() 到它们。 + +通过这种方式,现有的 Frida 工具的工作方式完全相同 —— 并且通过在 Portal 上启用 spawn-gating,可以指示任何连接的 Gadget 等待有人在应用所需的检测后 resume() 它。这与其他情况下 spawn-gating 的工作方式相同。 + +### 第二部分:实现 + +实现这一点非常有趣,不久之后第一个 PoC 就启动并运行了。不过,花了一些时间才弄清楚所有细节,但这最终 [crystallized][] 为以下内容: + +Portal 应公开两个不同的接口: + +1. Gadget 可以连接到的 ***cluster*** 接口,允许它们加入集群。 +2. 可选地还有一个 ***control*** 接口,控制器可以与之对话。例如 `frida-trace -H my.portal.com -n Twitter -i open` + +对于用户来说,这非常简单:只需从我们的 [releases][] 中获取 frida-portal 二进制文件,并在 Gadget 能够 [reach][] 的某台机器上运行它。然后将工具指向它 —— 就像它是一个普通的 frida-server 一样。 + +但这只是故事的一部分 —— 它将如何用于简单的用例。frida-portal CLI 程序实际上只不过是底层 *PortalService* 的一个瘦 CLI 包装器。这个 CLI 程序只有 [200 lines of code][] 多一点,其中很少是实际逻辑。 + +也可以使用我们的 frida-core 语言绑定(例如 Python 或 Node.js)来实例化 PortalService。这允许将其配置为不提供任何控制接口,而是访问其 ***device*** 属性。这是一个标准的 Frida Device 对象,可以在其上 enumerate_processes(), attach() 等。或者可以同时做这两件事。 + +使用 API 还提供了其他功能,但我们将稍后回到这些功能。 + +### 第三部分:TLS + +鉴于在公共互联网上运行 frida-portal 可能非常有用,同样清楚的是我们应该支持 TLS。由于我们已经在其他功能的依赖项中拥有 [glib-networking][],这使得添加它在足迹方面非常便宜。 + +在实现方面,[client][] 端只有一点点逻辑,服务器端的故事也同样简单。 + +对于 CLI 工具,只需传递 `--certificate=/path/to/pem` 即可。如果它是服务器,它需要一个带有公钥 + 私钥的 PEM 编码文件,它将接受来自传入客户端的任何证书。对于客户端,它也期望一个 PEM 编码文件,但只有受信任 CA 的公钥,服务器的证书必须匹配或派生自该公钥。 + +在 API 级别,归结为: + +{% highlight python %} +import frida + +manager = frida.get_device_manager() +device = manager.add_remote_device("my.portal.com", + certificate="/path/to/pem/or/inline/pem-data") +session = device.attach("Twitter") +… +{% endhighlight %} + +### 第四部分:身份验证 + +与在公共互联网上运行 frida-portal 密切相关的下一个相当明显的功能是身份验证。在这种情况下,我们的服务器 CLI 程序现在支持 `--token=secret`,我们的 CLI 工具也是如此。 + +在 API 级别,这也非常简单: + +{% highlight python %} +import frida + +manager = frida.get_device_manager() +device = manager.add_remote_device("my.portal.com", + token="secret") +session = device.attach("Twitter") +… +{% endhighlight %} + +但是,如果您通过 API 实例化 PortalService,这会变得更加有趣,因为它可以轻松插入您自己的自定义身份验证后端: + +{% highlight python %} +import frida + +def authenticate(token): + # Where `token` might be an OAuth access token + # that is used to grab user details from e.g. + # GitHub, Twitter, etc. + user = … + + # Attach some application-specific state to the connection. + return { + 'name': user.name, + } + +cluster_params = frida.EndpointParameters(authentication=('token', "wow-such-secret")) +control_params = frida.EndpointParameters(authentication=('callback', authenticate)) +service = frida.PortalService(cluster_params, control_params) +{% endhighlight %} + +EndpointParameters 构造函数还支持其他选项,例如 `address`, `port`, `certificate` 等。 + +### 第五部分:离线模式 + +这引出了我们的下一个挑战,即如何处理瞬态连接问题。我确实确保在 [PortalClient][] 中实现自动重连逻辑,这就是 Gadget 用于连接到 PortalService 的东西。 + +但是,即使 Gadget 重新连接到 Portal,在此期间加载的脚本应该发生什么?如果控制器与 Portal 断开连接怎么办? + +我们现在有一个处理这两种情况的解决方案。但它是可选的,因此旧行为仍然是默认行为。 + +它是这样完成的: + +{% highlight python %} +session = device.attach("Twitter", + persist_timeout=30) +{% endhighlight %} + +现在,一旦发生某些连接故障,脚本将保持在远端加载,但发出的任何消息都将排队。在上面的示例中,客户端有 30 秒的时间重新连接,然后脚本被卸载并且数据丢失。 + +然后控制器将订阅 `Session.detached` 信号以便能够处理这种情况: + +{% highlight python %} +def on_detached(reason, crash): + if reason == 'connection-terminated': + # Oops. Better call session.resume() + +session.on('detached', on_detached) +{% endhighlight %} + +一旦 `session.resume()` 成功,任何缓冲的消息都将被传递,生活又变得美好。 + +上面的示例确实掩盖了一些细节,例如我们当前 Python 绑定挑剔的线程约束,但请查看 [here][] 的完整示例。(一旦我们将 Python 绑定从同步 API 移植到 async/await,这将变得简单得多。) + +### 第六部分:延迟和瓶颈 + +好了,接下来我们在美国的数据中心运行了一个 Portal,但 Gadget 在我朋友在西班牙的家里,而我正试图从挪威使用 frida-trace 控制它。如果来自西班牙的脚本消息必须两次穿越大西洋,那将是一种耻辱,不仅因为延迟,还因为我下个月必须支付的 AWS 账单。因为我现在正在转储内存,那可是相当大的流量。 + +这有点难,但多亏了 [libnice][],一个建立在 [GLib][] 之上的轻量级且成熟的 ICE 实现,我们可以继续使用它。鉴于 GLib 已经是我们堆栈的一部分 —— 因为它是我们进行 C 编程的标准库(并且我们的 Vala 代码编译为依赖于 GLib 的 C 代码)—— 这是一个完美的契合。就足迹而言,这是非常好的消息。 + +作为用户,只需传递 `--p2p` 以及 STUN 服务器即可: + +{% highlight sh %} +$ frida-trace \ + -H my.portal.com \ + --p2p \ + --stun-server=my.stunserver.com \ + -n Twitter \ + -i open +{% endhighlight %} + +(也支持 TURN 中继。) + +API 方面的故事如下所示: + +{% highlight python %} +session.setup_peer_connection(stun_server="my.stunserver.com") +{% endhighlight %} + +这就是全部内容! + +### 第七部分:只有 Gadget 被邀请参加聚会吗? + +您可能已经注意到,到目前为止,我们的 Gadget 一直是一个反复出现的主题。我不太热衷于添加仅适用于一种 [mode][] 的功能,例如仅适用于注入模式但不适用于嵌入模式。所以这是我很早就想到的事情,即 Portal 必须是一个普遍可用的功能。 + +假设我的伙伴正在意大利的客厅里逆向他 iPhone 上的目标,我想加入其中的乐趣,他可以继续运行: + +{% highlight sh %} +$ frida-join -U ReversingTarget my.portal.com cert.pem secret +{% endhighlight %} + +现在我可以使用 Frida REPL 跳进去: + +{% highlight sh %} +$ frida \ + -H my.portal.com \ + --certificate=cert.pem \ + --token=secret \ + --p2p \ + --stun-server=my.stunserver.com \ + -n ReversingTarget +{% endhighlight %} + +如果我的伙伴想使用 API 加入 Portal,他可以: + +{% highlight python %} +session = frida.get_usb_device().attach("ReversingTarget") +membership = session.join_portal("my.portal.com", + certificate="/path/to/cert.pem", + token="secret") +{% endhighlight %} + +### 第八部分:Web + +在 Frida 诞生之前,我就一直想构建一个在线协作逆向应用程序。在 Frida 最开始的时候,我构建了一个集成了聊天、控制台等的桌面 GUI。然而,我并不充裕的业余时间是一个挑战,所以我最终摆脱了 GUI 代码并决定专注于 API。 + +现在我们到了 2021 年,单页应用程序 (SPA) 在许多情况下可能是一个非常有吸引力的选择。我还注意到已经有不少基于 Frida 构建的 SPA,这非常令人兴奋!但是当我独自玩弄 SPA 时,我注意到不得不编写中间件是相当乏味的。 + +好吧,在 Frida 15 中,我必须进行一些协议更改以适应我目前涵盖的功能,所以这似乎也是真正打破协议并进行主要版本升级的正确时机。这是我很长一段时间以来一直试图避免的事情,因为我知道它们对每个人(包括我自己)来说是多么痛苦。 + +所以现在浏览器终于可以加入其中的乐趣,而无需任何中间件: + +{% highlight js %} +async function start() { + const ws = wrapEventStream(new WebSocket(`ws://${location.host}/ws`)); + const bus = dbus.peerBus(ws, { + authMethods: [], + }); + + const hostSessionObj = await bus.getProxyObject('re.frida.HostSession15', + '/re/frida/HostSession'); + const hostSession = hostSessionObj.getInterface('re.frida.HostSession15'); + + const processes: HostProcessInfo[] = await hostSession.enumerateProcesses({}); + console.log('Got processes:', processes); + + const target = processes.find(([, name]) => name === 'hello2'); + if (target === undefined) { + throw new Error('Target process not found'); + } + const [pid] = target; + console.log('Got PID:', pid); + + const sessionId = await hostSession.attach(pid, { + 'persist-timeout': new Variant('u', 30) + }); + … +} +{% endhighlight %} + +(完整示例可以在 [examples/web_client][] 中找到。) + +这意味着 Frida 的网络协议现在是基于 WebSocket 的,因此浏览器终于可以直接与正在运行的 Portal/frida-server 对话,而无需中间有任何中间件或网关。 + +但我不想这只是一个半生不熟的故事,所以我确保 [peer-to-peer implementation][] 建立在 WebRTC 数据通道上 —— 这样即使是浏览器也可以以最小的延迟进行通信,并有助于保持较低的 AWS 账单。 + +### 第九部分:资产 + +一旦我们构建了一个与我们的 Portal 配套的 Web 应用程序,它是原生讲 WebSocket 的,因此也是 HTTP,我们也可以让从同一台服务器为该 SPA 提供服务变得超级容易: + +{% highlight sh %} +$ ./frida-portal --asset-root=/path/to/web/app +{% endhighlight %} + +这在 API 级别也很容易: + +{% highlight python %} +control_params = frida.EndpointParameters(asset_root="/path/to/web/app") +service = frida.PortalService(cluster_params, control_params) +{% endhighlight %} + +### 第十部分:协作 + +一旦我们有了一个控制器,比如说一个 Web 应用程序,自然的下一步就是我们可能想要协作功能,其中该 SPA 的多个运行实例能够相互通信。 + +鉴于我们在控制器和 PortalService 之间已经有了 TCP 连接,让开发人员使用该通道实际上是免费的。对于许多用例,需要额外的信令通道会带来很多可以避免的复杂性。 + +这就是新的 `Bus` API 发挥作用的地方: + +{% highlight python %} +import frida + +def on_message(message, data): + # TODO: Handle incoming message. + pass + +manager = frida.get_device_manager() +device = manager.add_remote_device("my.portal.com") +bus = device.bus + +bus.on('message', on_message) +bus.attach() + +bus.post({ + 'type': 'rename', + 'address': "0x1234", + 'name': "EncryptPacket" +}) +bus.post({ + 'type': 'chat', + 'text': "Hey, check out EncryptPacket everybody" +}) +{% endhighlight %} + +在这里,我们首先附加一个消息处理程序,以便我们可以从 Portal 接收消息。 + +然后我们调用 `attach()` 以便 Portal 知道我们有兴趣与它通信。(我们不希望它向不使用 Bus 的控制器发送消息,例如 frida-trace。) + +最后,我们 `post()` 两种不同的消息类型。由 PortalService 决定如何处理它们。 + +所以这意味着远程 PortalService 需要通过 API 实例化,因为传入的消息需要被处理 —— Portal 不会自己将它们转发给其他控制器。 + +不过不用担心,这很容易: + +{% highlight python %} +import frida +import sys + +def on_message(connection_id, message, data): + # TODO: Handle incoming message. + pass + +cluster_params = frida.EndpointParameters() +control_params = frida.EndpointParameters() +service = frida.PortalService(cluster_params, control_params) +service.on('message', on_message) +service.start() + +sys.stdin.read() +{% endhighlight %} + +在 `on_message()` 中,它应该查看 `message` 并决定做什么。 + +它可能会选择回复发送消息的控制器: + +{% highlight python %} +service.post(connection_id, { + 'type': 'rename-rejected', + 'reason': "Not authorized" +}) +{% endhighlight %} + +另一件有用的事情是每当有人在其 Bus 对象上调用 attach() 时发送欢迎消息: + +{% highlight python %} +def on_subscribe(connection_id): + service.post(connection_id, { + 'type': 'welcome', + 'users': [user.nick for user in connected_users] + }) + +service.on('subscribe', on_subscribe) +{% endhighlight %} + +根据您的应用程序,您可能还需要一种方法向所有连接到其 Bus 的控制器广播消息: + +{% highlight python %} +service.broadcast({ + 'type': 'announce', + 'text': "Important Service Announcement" +}) +{% endhighlight %} + +您还可以向控制器子集 `narrowcast()` 消息: + +{% highlight python %} +service.narrowcast("#reversing", { + 'type': 'chat', + 'sender': user.nick, + 'text': "Hello everyone" +}) +{% endhighlight %} + +这意味着任何标记为 `#reversing` 的控制器连接都将收到该消息。标记是这样完成的: + +{% highlight python %} +service.tag(connection_id, "#reversing") +{% endhighlight %} + +此类标签可以基于操作添加,例如控制器发送“join”消息以加入频道。它们也可以基于身份验证应用,以便只有属于某个 GitHub 组织的连接才会收到该消息 —— 仅举一例。 + +最后,在集群方面,加入 Portal 时还可以指定访问控制列表 (ACL)。ACL 是一个字符串数组,指定标签,这些标签将授予控制器发现给定进程并与之交互的权限。这意味着需要为每个应被授予访问某个节点/节点组权限的控制器使用 `service.tag()`。 + +这几乎就是全部内容。有关更全面的示例,请查看 [examples/portal_server.py][] 和 [examples/portal_client.py][],它们实现了类似 IRC 的聊天服务。 + +## 系统参数 + +早在 5 月,我与 [@Hexploitable][] 聊过,他正在开发一个工具,他需要根据设备运行的是 iOS 还是 Android 来选择 Device 对象。这是一个过去曾被请求过的功能,感觉是时候最终解决它了。 + +虽然可以执行 device.attach(0) 并在系统会话中加载脚本,以便在 Frida 本身内部运行代码(例如在远程 frida-server 中),但这有点乏味。如果 Device 代表受限/非 root 设备,这也行不通,因为在那里的代码执行受到更多限制。 + +所以在集思广益之后,@Hexploitable 开始致力于实现它,并很快达到了第一个草案工作的地步。这后来由我进行了改进,并在 Portals 功能最终落地后不久合并。 + +API 很简单,并且将来很容易扩展: + +{% highlight sh %} +$ python3 -c 'import frida; import json; \ + print(json.dumps(frida.query_system_parameters()))' \ + | jq +{% endhighlight %} + +![macOS Device](/img/query-system-parameters-macos.png "Output for a macOS Device") + +如果我连接一个受限的 iOS 设备,我也可以查询它: + +{% highlight sh %} +$ python3 -c 'import frida; import json; \ + device = frida.get_usb_device(); \ + print(json.dumps(device.query_system_parameters()))' \ + | jq +{% endhighlight %} + +![iOS Device](/img/query-system-parameters-ios.png "Output for an iOS Device") + +这里要注意的一个重要细节是 `access: 'jailed'`。这就是您如何确定您是否通过我们对受限 iOS/Android 系统的支持(即仅限于可调试的应用程序)访问设备,还是实际与远程 frida-server 对话 —— 这就是 `access: 'full'` 的意思。 + +对于 Android 来说,事情还没有那么丰富(顺便说一句,欢迎 PR!),但仍然有很多有用的细节: + +![Android Device](/img/query-system-parameters-android.png "Output for an Android Device") + +如果是符合 LSB 的发行版,我们还可以识别特定的 Linux 发行版: + +![Ubuntu Device](/img/query-system-parameters-ubuntu.png "Output for an Ubuntu Device") + +最后但并非最不重要的一点是 Windows: + +![Windows Device](/img/query-system-parameters-windows.png "Output for a Windows Device") + +## 应用程序和进程参数 + +另一个很酷的想法是在一些即兴聊天后开始形成的,当时 [@pancake][] 告诉我,知道已安装 iOS 应用程序的特定版本会很有用。 + +由于我在开发 Portals 功能时已经在很多方面破坏了协议,这似乎也是打破它更多并避免以后再次痛苦的主要版本升级的好时机。 + +快进一点,结果如下:我们的 `Application` 和 `Process` 对象不再有任何 `small_icon` 或 `large_icon` 属性,但它们现在有一个 `parameters` 字典。 + +默认情况下,使用 `enumerate_applications()`,事情看起来很熟悉: + +![enumerate_applications()](/img/enumerate-applications-ios-minimal.png "enumerate_applications()") + +但是通过将其更改为 `enumerate_applications(scope='metadata')`,事情变得更加有趣: + +![enumerate_applications(scope='metadata')](/img/enumerate-applications-ios-metadata.png "enumerate_applications(scope='metadata')") + +在这里我们可以看到 iOS Twitter 应用程序的版本和构建号,其应用程序包在文件系统上的位置,它拥有的容器,它是当前最前端的应用程序,它启动了多久等。 + +我们还可以将其调高到 `enumerate_applications(scope='full')` 并获取图标: + +![enumerate_applications(scope='full')](/img/enumerate-applications-ios-full.png "enumerate_applications(scope='full')") + +如果 query_system_parameters() 报告 `access: 'jailed'`,则 `debuggable: true` 参数非常有用,因为这意味着您的应用程序可能希望过滤应用程序列表以仅显示它能够 spawn() 和/或 attach() 的应用程序,或者可能更突出地显示可调试应用程序以提供更好的用户体验。 + +可能还值得一提的是,`get_frontmost_application()` 现在也支持传递 `scope`。 + +熟悉旧 API 的人可能已经注意到,图标现在可能以压缩形式作为 PNG 交付。以前这总是未压缩的 RGBA 数据,iOS 端会进行 PNG 解码并缩小到两个固定分辨率(16x16 和 32x32)。 + +所有这些意味着我们会浪费大量的 CPU 时间、内存和带宽来包含图标,即使所有数据最终都会进入不使用它的 CLI 工具。所以现在有了 Frida 15,您可能会注意到应用程序和进程列表速度要快得多。即使您确实请求图标,它也应该比以前更快,因为我们不进行任何解压缩和缩小。 + +那是应用程序列表。以上所有内容也适用于进程列表,这就是 `enumerate_applications(scope='full')` 现在可能的样子: + +![enumerate_processes(scope='full')](/img/enumerate-processes-ios-full.png "enumerate_processes(scope='full')") + +在这里也很清楚,Twitter 应用程序当前是最前端的,其父 PID 是 launchd (PID 1),它作为哪个用户运行,何时启动等。 + +您可能想知道为什么 `applications` 是一个数组,答案可能最好用 Android 的一个例子来说明: + +![enumerate_processes(scope='full')](/img/enumerate-processes-android-full.png "enumerate_processes(scope='full')") + +“com.android.phone” 进程实际上托管了六个不同的“应用程序”! + +再一次,最后但并非最不重要的一点是,我没有忘记 Windows: + +![enumerate_processes(scope='full')](/img/enumerate-processes-windows-full.png "enumerate_processes(scope='full')") + +这就是“scope”选项。还有另一个,用于 UI。这个想法是,UI 可能希望快速获取应用程序/进程列表,并且在用户与特定条目交互或将条目子集滚动到视图中之前,实际上可能不需要元数据/图标。因此,我们现在提供一个选项来支持此类用例。 + +假设我们只想获取两个特定应用程序的元数据,我们现在可以这样做: + +{% highlight python %} +ids = [ + "com.atebits.Tweetie2", + "no.sparebank1.mobilbank" +] +apps = device.enumerate_applications(identifiers=ids, + scope='full') +{% endhighlight %} + +![enumerate_applications(identifiers=x)](/img/enumerate-selected-applications.png "enumerate_applications(identifiers=x)") + +我们也支持进程列表的相同功能,如下所示: + +{% highlight python %} +processes = device.enumerate_processes(pids=[1337, 1338], + scope='full') +{% endhighlight %} + +## Portals 和应用程序/进程参数 + +现在我们已经涵盖了应用程序参数 **和** portals,有一个重要的细节值得一提:鉴于在 PortalService 的情况下实现 query_system_parameters() 没有多大意义,因为它正在从任意数量的(可能是远程的)系统浮现进程,我们可以使用应用程序/进程参数来填补这一空白。 + +这意味着来自 PortalService 的任何 Application 和 Process,如果 `scope` 设置为 `metadata` 或 `full`,将提供一个名为 `system` 的参数,其中包含该特定应用程序/进程的系统参数。这样应用程序仍然可以提前知道它是否对特定进程感兴趣。 + +## 受限 iOS 和 Android 改进 + +我在实现应用程序和进程参数功能时玩得很开心,并试图看看我能把受限(非 root)和越狱(root)之间的差距缩小到多大。例如在 Android 上,我们在非 root 代码路径中甚至没有获取应用程序标签。这是因为我们依赖于通过 ADB 运行 shell 命令,而在这种情况下我找不到获取标签的方法。 + +shell 命令路线非常脆弱,因为大多数工具以供人类使用而不是机器使用的格式输出详细信息。显然,随着 Android 的发展,这种输出可能会发生变化。 + +因此,我们现在有一个微小的预构建 .dex,我们将其复制并运行,获取元数据只是向该辅助进程进行 RPC 调用的问题。这意味着我们能够为非 root 提供与我们在 root 情况下提供的所有相同的详细信息,在 root 情况下我们在 Android 端运行 frida-server。 + +另一件值得一提的事情是,我们不再将 Android 的启动器视为最前端的应用程序,这意味着这现在与我们在 iOS 上的行为一致,其中 SpringBoard 从不被视为最前端的应用程序。 + +作为这些重大更改的一部分,我还添加了在 Android 上获取图标的代码,包括非 root 和 root,因此此功能不再仅限于 iOS、macOS 和 Windows。 + +我们没有为受限 iOS 提供图标,但那个功能差距现在也已弥合。然而,受限和越狱 iOS 之间仍然存在一个区别:`ppid` 和 `user` 参数在受限情况下不可用,因为这没有被我目前知道的任何 lockdown/DTX API 公开。但除此之外,情况相当不错。 + +## i/macOS 上大幅改进的回溯 + +感谢 [@hot3eed][] 提出的非常令人兴奋的拉取请求,我们现在有了一个使用 Objective-C 运行时的 i/macOS 符号化回退。通过这种方式,我们可能能够将其解析为 Objective-C 方法,而不是显示 `module!0x1234`。耶! + +我们还得到了 [@mrmacete][] 的另一个很棒的贡献,其中 NativeCallback 现在总是公开一个上下文,所以你可以做 `Thread.backtrace(this.context)` 并期望它在所有情况下都能工作。 + +这以前只有在 NativeCallback 用作 Interceptor 替换时才可能。因此,如果您使用 ObjC.implement() 来 swizzle Objective-C API,您实际上无法从该 NativeCallback 捕获回溯。所以这是一个超级令人兴奋的改进! + +## Android 上未提取的本机库 + +对于那些在 Android 上使用 Frida 的人,您可能遇到过本机库不驻留在文件系统上,而是直接从应用程序的 .apk 加载的应用程序。感谢 [@P-Sc][] 的巨大贡献,我们现在透明地支持这一点 —— 不需要更改您现有的检测代码。 + +## 升级的操作系统支持 + +我们现在还支持 macOS Monterey、iOS 15 和 Android 12 的最新测试版。特别感谢 [Corellium][] 的 [@alexhude][] 帮助调试和测试 iOS 15 上的东西,以及 [@pengzhangdev][] 贡献了一个修复 frida-java-bridge 以支持 Android 12。 + +## 联网 iOS 设备 + +另一个被多次请求的功能是支持联网 iOS 设备。如果您不想通过整天插着电源来破坏 iPhone/iPad 的电池,这非常棒。这个功能最棒的地方在于它“就是好用” —— 如果您运行 `frida-ls-devices`,您应该能看到它们。 + +值得一提的只有两个陷阱:您现在可能拥有两个具有相同 ID 的不同 Device 对象,以防联网 iOS 设备在通过网络可达的同时也已插入。 + +例如: + +{% highlight sh %} +$ frida-ls-devices +Id Type Name +------------------------- ------ ------------------------------------- +local local Local System +00008027-xxxxxxxxxxxxxxxx usb iPad +socket remote Local Socket +00008027-xxxxxxxxxxxxxxxx remote iOS Device [fe80::146f:75af:d79:630c] +{% endhighlight %} + +因此,如果您使用 `-U` 或 `frida.get_usb_device()`,事情将像以前一样工作,您将通过 USB 使用您的设备。但是,如果您想使用联网设备,那么通过 ID 解析意味着 USB 条目将优先,因为它通常在设备列表中位于联网设备之前。 + +这意味着您还需要检查它的 `type`。我们的 CLI 工具尚未提供开关来执行此操作,但如果有人感兴趣,这将是一个受欢迎的拉取请求! + +第二个陷阱是 frida-server 默认只监听环回接口,这意味着我们将无法通过网络连接到它。因此,如果您手动或通过 Cydia 使用我们的 iOS .deb,您将不得不编辑 `/Library/LaunchDaemons/re.frida.server.plist` 以添加 `--listen` 开关,然后使用 `launchctl` 重启它。 + +这也可能是您想要利用前面提到的新 TLS 和身份验证功能的情况,具体取决于您对网络环境的信任程度。 + +## EOF + +还有很多其他令人兴奋的更改,所以一定要查看下面的变更日志。 + +享受吧! + + +### 15.0.0 中的变化 + +- 引入 PortalService API 和守护进程,这是一个网络服务,用于编排由 Frida 检测的远程进程集群。实现 frida-server 兼容的控制接口,以及目标进程中的 agent 和 gadget 可以与之对话的集群接口。连接的控制器可以枚举进程,就像它们在运行 portal 的系统本地一样,并且能够 attach() 并启用 spawn-gating 以应用早期插桩。 +- 添加 Session.join_portal(),使与远程 PortalService 共享进程控制变得容易,与其他节点一起加入其集群。 +- 向 frida-gadget 添加“connect”交互,以便它也可以加入 PortalService 集群。 +- 添加 PortalClient,用于实现 Session.join_portal() 和 frida-gadget 的“connect”交互。连接到 PortalService 并加入其集群。在瞬态故障的情况下实现自动重连。还支持指定 ACL,这是 PortalService 必须要求连接的控制器至少拥有其中之一的标签列表。由应用程序根据例如身份验证来实现控制器的标记。 +- 添加 Device.bus API 以允许连接到 PortalService 的客户端与其交换特定于应用程序的消息。需要使用 API 实例化服务以连接消息处理程序和协议逻辑。 +- 添加 Session 持久性支持,通过在 attach() 到进程时指定非零“persist_timeout”选项来启用。当服务器随后检测到拥有会话的客户端断开连接时,它将允许脚本保持加载状态,直到达到超时(以秒为单位)。在此期间发出的任何脚本和调试器消息都将排队,如果客户端在达到超时之前返回,则稍后可能会传递这些消息。 +- 添加 TLS 支持,通过指定证书启用。在服务器端,这是一个带有公钥和私钥的 PEM,服务器将接受来自客户端的任何证书。但是对于客户端,这是一个带有受信任 CA 公钥的 PEM,服务器的证书必须匹配或派生自该公钥。 +- 添加身份验证支持,通过指定令牌启用。守护进程允许通过 CLI 选项指定静态令牌,API 允许插入自定义身份验证后端 —— 这意味着可以根据需要解释令牌。 +- 将网络协议移动到 WebSocket。 +- 添加协议级 keepalive。 +- 实现 WebRTC 数据通道兼容的点对点支持,通过在 Session 上调用 setup_peer_connection() 启用。这允许直接连接。 + + +[@insitusec]: https://twitter.com/insitusec +[Listen]: /docs/gadget/#listen +[crystallized]: https://twitter.com/oleavr/status/1393339683936608259 +[releases]: https://github.com/frida/frida/releases +[reach]: https://twitter.com/oleavr/status/1393666016793268228 +[200 lines of code]: https://github.com/frida/frida-core/blob/15.0.0/src/portal.vala#L1-L224 +[glib-networking]: https://gitlab.gnome.org/GNOME/glib-networking +[client]: https://github.com/frida/frida-core/blob/15.0.0/src/socket/service.vala#L15-L27 +[PortalClient]: https://github.com/frida/frida-core/blob/15.0.0/src/portal-client.vala +[here]: https://github.com/frida/frida-python/blob/15.0.0/examples/portal_client.py#L42-L61 +[libnice]: https://libnice.freedesktop.org/ +[GLib]: https://gitlab.gnome.org/GNOME/glib +[mode]: /docs/modes/ +[examples/web_client]: https://github.com/frida/frida-core/tree/15.0.0/examples/web_client +[peer-to-peer implementation]: https://github.com/frida/frida-core/blob/15.0.0/src/socket/p2p-broker.vala +[examples/portal_server.py]: https://github.com/frida/frida-python/blob/15.0.0/examples/portal_server.py +[examples/portal_client.py]: https://github.com/frida/frida-python/blob/15.0.0/examples/portal_client.py +[@Hexploitable]: https://twitter.com/Hexploitable +[@pancake]: https://twitter.com/trufae +[@hot3eed]: https://github.com/hot3eed +[@mrmacete]: https://twitter.com/bezjaje +[@P-Sc]: https://github.com/P-Sc +[@alexhude]: https://twitter.com/alexhude +[Corellium]: https://www.corellium.com/ +[@pengzhangdev]: https://github.com/pengzhangdev diff --git a/_i18n/cn/_posts/2021-09-03-frida-15-1-released.markdown b/_i18n/cn/_posts/2021-09-03-frida-15-1-released.markdown new file mode 100644 index 00000000..17423fc3 --- /dev/null +++ b/_i18n/cn/_posts/2021-09-03-frida-15-1-released.markdown @@ -0,0 +1,191 @@ +--- +layout: news_item +title: 'Frida 15.1 发布' +date: 2021-09-03 12:00:00 +0200 +author: hot3eed +version: 15.1 +categories: [release] +--- + +介绍 _全新的_ Swift 桥接!既然 Swift 自版本 5 以来已经是 ABI 稳定的,这个期待已久的桥接允许 Frida 与用 Swift 编写的二进制文件很好地配合。无论您 [consider][] Swift 是静态语言还是动态语言,有一件事是肯定的,随着这个 Frida 版本的发布,它变得更加动态了。 + +## 元数据 + +逆向工程师在开始逆向二进制文件时做的第一件事可能就是了解二进制文件定义的不同数据结构。因此,首先构建相当于 `ObjC.classes` 和 `ObjC.protocols` API 的 Swift 等效项是最有意义的。但是,由于 Swift 具有其他一等类型,即结构体和枚举,并且由于 Swift 运行时不提供反射原语,至少不像 Objective-C 那样,这意味着我们必须挖掘得更深一点。 + +幸运的是,Swift 编译器为二进制文件定义的每种类型发出元数据。在撰写本文时,此元数据捆绑在 `TargetTypeContextDescriptor` C++ 结构中,定义在 [include/swift/ABI/Metadata.h][] 中。此数据结构包括类型名称、其字段、其方法(如果适用)以及取决于手头类型的其他有用数据。这些数据结构由相对指针指向(定义在 [include/swift/Basic/RelativePointer.h][] 中)。在 Mach-O 二进制文件中,这些存储在 `__swift5_types` 部分中。 + +因此,要转储类型,Frida 基本上会迭代这些数据结构并沿途解析它们,这与 [dsdump][] 所做的非常相似,除了您不必为了修补它而构建 Swift 编译器。 + +Frida 还具有能够探测用 Swift 编写的内部 Apple dylib 的优势,这是因为我们不需要解析 `dyld_shared_cache`,这要归功于私有的 `getsectiondata` API,它让我们轻松获得部分偏移量。 + +一旦我们有了元数据,我们就能够轻松地为对象实例和不同类型的值创建 JavaScript 包装器。 + +## 约定 + +为了与 Objective-C 桥接相提并论,Swift 桥接必须支持调用 Swift 函数,这也被证明不是那么简单。 + +Swift 定义了自己的调用约定 `swiftcall`,简而言之,它试图尽可能高效。这意味着,不要在小于 4 个寄存器大小的结构体上浪费加载和存储指令。也就是说,直接在寄存器中传递这些类型的结构体。由于这可能会很快超额预订我们宝贵的 8 个参数寄存器(在 AARCH64 上为 `x0`-`x7`),它不使用第一个寄存器作为 `self` 参数。它还定义了一个 `error` 寄存器,被调用者可以在其中存储它们抛出的错误。 + +我们上面刚刚描述的内容在 Swift 编译器文档中被称为“物理降级”,它由后端 LLVM 实现。 + +物理降级之前的过程称为“语义降级”,即编译器前端弄清楚谁“拥有”一个值以及它是直接的还是间接的。有些结构体,即使它们可能小于 4 个寄存器,也必须间接传递,因为例如,它们是泛型的,因此它们的确切内存布局在编译时是未知的,或者因为它们包含必须始终在内存中的弱引用。 + +我们必须实现语义和物理降级才能调用 Swift 函数。物理降级是使用 JIT 编译的适配器函数实现的(感谢 `Arm64Writer` API),它执行必要的 `SystemV`-`swiftcall` 转换。语义降级是通过利用类型的元数据来确定我们是否应该直接传递值来实现的。 + +编译器 [docs][] 是了解有关调用约定的更多信息的绝佳资源。 + +## 拦截 + +因为 Swift 直接在寄存器中传递结构体,所以在寄存器和实际参数之间没有 1 对 1 的映射。 + +现在我们有了类型的 JavaScript 包装器,并且能够从 JS 运行时调用 Swift 函数,下一步很好的做法是扩展 `Interceptor` 以支持 Swift 函数。 + +对于未剥离的函数,我们使用简单的正则表达式来解析参数类型和名称,返回值也是如此。解析它们之后,我们检索类型元数据,弄清楚类型的布局,然后简单地为每个参数构造 JS 包装器,我们将 Swift 参数值传递给它,无论它占用多少个寄存器。 + +## EOF + +请注意,该桥接仍处于开发的早期阶段,因此: + - 目前仅支持 Darwin arm64(e)。 + - 性能尚未达到最佳状态,某些边缘情况可能无法正确处理,并且可能会出现一些错误。 + - API 有可能在短期到中期内以破坏性方式更改。 + - 非常欢迎 PR 和问题! + +请参阅 [documentation][] 以获取有关当前 API 的最新资源。 + +享受吧! + + +### 15.1.0 中的变化 + +- 实现 Swift 桥接,允许 Frida: + - 探索 Swift 模块以及在其中实现的类型,即类、结构体、枚举和协议。 + - 为对象实例和值创建 JavaScript 包装器。 + - 从 JavaScript 运行时调用使用 `swiftcall` 调用约定的函数。 + - 拦截 Swift 函数并自动解析其参数和返回值。 +- 修复 i/macOS 回归,其中与 iOS 15 支持相关的更改最终破坏了对附加到 Apple 系统守护程序的支持。 +- gadget: 在连接模式下添加 interaction.parameters。然后,这些参数被“反射”到 `parameters.config` 下的应用程序信息中。感谢 [@mrmacete][]! + +### 15.1.1 中的变化 + +- gumjs: 修复 V8 运行时中的 Swift 生命周期逻辑。 + +### 15.1.2 中的变化 + +- control-service: 修复信号接线,以便 Device.output 等信号由远程 frida-server 正确发出。感谢 [@mrmacete][]! +- gadget: 修复“runtime”选项,该选项在导致 Frida 15 的重构期间被遗忘了。 +- relocator: 优化 x86 RIP 相对代码的处理,通过尽可能简单地调整偏移量。 +- gumjs: 添加 ESM 支持,以便像 frida-compile 这样的工具可以输出更好的代码。 +- gumjs: 解析后丢弃源代码。 +- gumjs: 编译为 QuickJS 字节码时堵塞泄漏。 +- java: 公开 JNIEnv->GetDirectBufferAddress。感谢 [@pandasauce][]! + +### 15.1.3 中的变化 + +- objc: 修复 Proxy respondsToSelector 实现。感谢 [@hot3eed][]! +- gumjs: 修复模块丢失时 V8 运行时的崩溃。 +- gumjs: 发出由 ESM 入口点抛出的 V8 异常。 + +### 15.1.4 中的变化 + +- gumjs: 修复 QuickJS 运行时中与弱引用回调相关的双重释放。感谢 [@mrmacete][]! +- gumjs: 在不相关的 NativeCallback 调用中忽略 Interceptor 上下文。通过这种方式,来自调用堆栈更高处的无效 Interceptor 上下文将被安全地忽略,而有利于最小但正确的回调上下文。感谢 [@mrmacete][]! +- gumjs: 修复 ESM 模块名称规范化逻辑。 +- gumjs: 为 cwd、home 和 tmp 目录添加 Process getter。 +- swift: 将元数据缓存性能提高约 3 倍。感谢 [@hot3eed][]! +- node: 发布 v15 而不是 v13 的 Electron 预构建。 + +### 15.1.5 中的变化 + +- gumjs: 修复 QuickJS 字符串化大数字时的崩溃。感谢 [@vfsfitvnm][]! +- gumjs: 向 CModule 公开 GError 和 GIConv。感谢 [@0xDC00][]! +- droidy: 支持 ADB 服务器主机/端口环境变量。感谢 [@amirpeles90][]! +- swift: 改进非 Darwin 上的加载行为。 + +### 15.1.6 中的变化 + +- swift: 修复非 Darwin 上加载期间的崩溃。感谢 [@hot3eed][]! + +### 15.1.7 中的变化 + +- swift: 修复旧操作系统版本上的 CoreSymbolication 集成。感谢 [@hot3eed][]! +- python: 为 Android/ARM 添加 setup.py 下载逻辑。不过,我们的 CI 尚未构建和上传此类二进制文件。 + +### 15.1.8 中的变化 + +- darwin: 添加对在 SRD 环境中工作的支持。感谢 [@Nessphoro][]! +- darwin: 添加对使用较新 iOS SDK 构建的支持。 + +### 15.1.9 中的变化 + +- x86-relocator: 修复 RIP 相对指令的修补。这是 15.1.2 中引入的回归,导致 Stalker 变得不可靠。 +- portal-service: 每当从 PortalService 取消设置会话 ID 时,始终删除 ClusterNode 会话。这避免了 NULL 解引用和泄漏。感谢 [@mrmacete][]! +- frida-portal: 修复 --help 输出中的拼写错误。 + +### 15.1.10 中的变化 + +- p2p: 优雅地处理不支持的 ICE 候选者。 +- p2p: 暂时禁用 ICE-TCP。 +- p2p: 重做 PeerSocket 以修复同步问题。 +- socket: 修复 WebService 拆除。 +- vala: 优化服务器端 GDBus 回复处理。这意味着我们的 RPC 和网络协议变得不那么健谈/性能更高。 +- x86-writer: 在间接分支之后/之中添加 UD2 指令。 +- x86-writer: 尽可能发出更大的 NOP。 +- stalker: 改进 x86/64 后端的性能。 +- objc-api-resolver: 防止已处置的 ObjC 类。感谢 [@mrmacete][]! +- gumjs: 修复 V8 Interceptor.{replace,revert}() 回归。感谢 [@3vilWind][]! +- gumjs: 使用 regsAccessed 和 operand.access 扩展 Instruction API。感谢 [@3vilWind][]! + +### 15.1.11 中的变化 + +- x86-writer: 添加 put_sahf() 和 put_lahf()。 +- x86-relocator: 修复超出范围的 Jcc 分支目标的处理。感谢 [@0xDC00][]! +- stalker: 优化目标地址检索。 +- stalker: 避免昂贵的 XCHG 指令。 +- stalker: 优化 IC 序言以使用 SAHF/LAHF。 +- memory: 改进 scan() 以支持正则表达式模式。感谢 [@hot3eed][]! +- kernel: 在支持的地方从 all_image_info 获取基址。感谢 [@mrmacete][]! +- windows: 提高 dbghelp backtracer 的可靠性。感谢 [@HonicRoku][]! + +### 15.1.12 中的变化 + +- agent: 修复在 NO_REPLY_EXPECTED 调用卸载时的挂起,我们会等待发送回复。由于服务器端 DBus 代码由 Vala 编译器生成,并且它以前(在 Frida <= 15.1.9 中)忽略 NO_REPLY_EXPECTED,因此此错误未被注意到。 +- android: 迁移到 NDK r24 Beta 1。 + +### 15.1.13 中的变化 + +- linux: 改进 glibc 系统上的模块解析。 +- fruity: 修复 dyld v4 案例(越狱 iOS 15.x)上的 spawn。感谢 [@mrmacete][]! +- objc-api-resolver: 通过互斥锁防止 objc_disposeClassPair()。感谢 [@mrmacete][]! +- gumjs: 更新 Kernel.scan\*() 以匹配 Memory.scan\*()。感谢 [@hot3eed][]! +- stalker: 修复 x86 上发出的分支操作码。 +- stalker: 修复 Thumb IT AL 的处理。 +- stalker: 在 x86 上通过 PLT 处理排除的 Linux 调用。 +- stalker: 修复 x86 上的 Linux 异常处理。 +- java: 添加 Java.backtrace(),目前没有任何 API 稳定性保证。 + +### 15.1.14 中的变化 + +- backtracer: 改进模糊回溯器以也包括直接调用者,并在已知时避免走过堆栈末尾。 +- windows: 实现 Thread.try_get_ranges()。 +- linux: 实现 Thread.try_get_ranges()。 +- ios: 在 SRD 系统上与 launchd 签到。 +- stalker: 修复 x86 上调用深度代码中的意外破坏。 +- node: 也发布 v17 的 Node.js 预构建。 + + +[consider]: https://youtu.be/0rHG_Pa86oA?t=36 +[include/swift/ABI/Metadata.h]: https://github.com/apple/swift/blob/52e852a7a9758e6edcb872761ab997b552eec565/include/swift/ABI/Metadata.h +[dsdump]: https://github.com/DerekSelander/dsdump +[include/swift/Basic/RelativePointer.h]: https://github.com/apple/swift/blob/52e852a7a9758e6edcb872761ab997b552eec565/include/swift/Basic/RelativePointer.h +[docs]: https://github.com/apple/swift/blob/52e852a7a9758e6edcb872761ab997b552eec565/docs/ABI/CallingConvention.rst +[documentation]: https://github.com/frida/frida-swift-bridge/blob/master/docs/api.md +[@mrmacete]: https://twitter.com/bezjaje +[@pandasauce]: https://github.com/pandasauce +[@hot3eed]: https://github.com/hot3eed +[@vfsfitvnm]: https://github.com/vfsfitvnm +[@0xDC00]: https://github.com/0xDC00 +[@amirpeles90]: https://github.com/amirpeles90 +[@Nessphoro]: https://github.com/Nessphoro +[@3vilWind]: https://github.com/3vilWind +[@HonicRoku]: https://github.com/HonicRoku diff --git a/_i18n/cn/_posts/2022-02-01-frida-15-1-15-released.markdown b/_i18n/cn/_posts/2022-02-01-frida-15-1-15-released.markdown new file mode 100644 index 00000000..75da46f0 --- /dev/null +++ b/_i18n/cn/_posts/2022-02-01-frida-15-1-15-released.markdown @@ -0,0 +1,193 @@ +--- +layout: news_item +title: 'Frida 15.1.15 发布' +date: 2022-02-01 00:46:40 +0100 +author: oleavr +version: 15.1.15 +categories: [release] +--- + +此版本中有许多令人兴奋的位。让我们直接潜入。 + +## FreeBSD + +我们的雄心是支持我们的用户关心的所有平台。在这个版本中,我想播下第一颗种子,将其扩展到 BSD。所以我现在很高兴地宣布 Frida 终于也支持 FreeBSD 了!🎉 + +目前我们只支持 x86_64 和 arm64,但如果有任何人有兴趣提供帮助,扩展到其余架构是很简单的。 + +移植工作导致了一些架构上的改进,并总体上提高了基于 ELF 的操作系统的健壮性。它还给了我一些关于如何改进我们的 Linux 注入器以支持注入容器的想法,这是我以后想做的事情。 + +## Stalker 性能 + +早在 15.1.10 中,Stalker 在 x86/64 上获得了巨大的性能提升。在这个版本中,同样的想法已应用于我们的 arm64 后端。这包括改进的局部性、更好的内联缓存等。有人告诉我,那时我们能够在 FuzzBench 中击败或匹配 QEMU,现在我们在 arm64 上也应该处于良好状态。我们还设法在改进稳定性的同时做到了这一点。令人兴奋! + +## GObject Introspection + +早在 14.1 中,[@meme][] 就为 [GObject Introspection][] 连接了构建系统支持。这意味着我们拥有所有 API 的机器可读描述,这使我们可以利用现有的语言 [binding][] 基础设施,甚至免费获得 [auto-generated reference docs][]。 + +此版本向 Gum 添加了大量注释和文档字符串,我们现在比以往任何时候都更接近拥有自动生成的参考文档。在发布生成的文档之前还有一些工作要做,但并不遥远。如果有人有兴趣参与,请查看 Gum 的 [CI][] 并查看 GObject Introspection 输出的警告。 + +## Meson 子项目支持 + +我非常喜欢 Meson 构建系统的一点是它对 [subprojects][] 的支持。Gum 现在支持作为子项目使用。你们中的一些人可能已经通过其 [devkit binaries][] 使用 Gum,现在您有了一个甚至更简单的全新选项。 + +与使用 devkit 相比,主要优势在于所有内容都是从源代码构建的,因此很容易试验代码。 + +假设我们有一个 `hello.c` 文件,其中包含: + +{% highlight c %} +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +static int (* open_impl) (const char * path, int oflag, ...); +static int replacement_open (const char * path, int oflag, ...); + +int +main (int argc, + char * argv[]) +{ + gum_init (); + + GumInterceptor * interceptor = gum_interceptor_obtain (); + + gum_interceptor_begin_transaction (interceptor); + + open_impl = dlsym (RTLD_DEFAULT, "open"); + gum_interceptor_replace (interceptor, open_impl, replacement_open, + NULL, NULL); + + gum_interceptor_end_transaction (interceptor); + + close (open_impl ("/etc/hosts", O_RDONLY)); + close (open_impl ("/etc/fstab", O_RDONLY)); + + return 0; +} + +static int +replacement_open (const char * path, + int oflag, + ...) +{ + printf ("!!! open(\"%s\", 0x%x)\n", path, oflag); + + return open_impl (path, oflag); +} +{% endhighlight %} + +要构建它,我们可以在它旁边创建一个 `meson.build`,内容如下: + +{% highlight meson %} +project('hello', 'c') +gum = dependency('frida-gum-1.0') +executable('hello', 'hello.c', dependencies: [gum]) +{% endhighlight %} + +并创建包含以下内容的 `subprojects/frida-gum.wrap`: + +{% highlight ini %} +[wrap-git] +url = https://github.com/frida/frida-gum.git +revision = main +depth = 1 + +[provide] +dependency_names = frida-gum-1.0, frida-gum-heap-1.0, frida-gum-prof-1.0, frida-gumjs-1.0 +{% endhighlight %} + +如果您尚未安装 Meson 和 Ninja,请运行 `pip install meson ninja`。 + +然后,构建并运行: + +{% highlight sh %} +$ meson setup build +$ meson compile -C build +$ ./build/hello +!!! open("/etc/hosts", 0x0) +!!! open("/etc/fstab", 0x0) +{% endhighlight %} + +## 足迹 + +我们付出了很多努力来确保 Frida 可以从桌面一直扩展到嵌入式系统。在这个版本中,我花了一些时间分析我们的二进制足迹,并基于此,我最终进行了一系列调整和构建选项,以减少我们的足迹。 + +我很好奇我可以让仅使用 Interceptor 的 [Gum Hello World][] 程序变得多小。最终结果是在带有 Thumb 指令的 32 位 ARM 上测量的,其中 Gum 及其依赖项是静态链接的,唯一的外部依赖项是系统的 libc。 + +结果小至 55K (!),这让我非常兴奋。我所做的是在 Gum、GLib 和 Capstone 中引入新的构建选项。对于 Gum,我们现在支持“diet”模式,在这种模式下,我们不使用 GObject 并仅提供纯 C API。这意味着它不支持 GObject Introspection 和花哨的语言绑定。这也意味着我们不提供完整的 Gum API,但这可以在将来扩展。 + +同样,对于 GLib,也有一种新的“diet”模式,归结为禁用其切片分配器、调试功能以及其他一些类似的微小调整。 + +至于 Capstone,我最终引入了一个新的“profile”选项,可以设置为“tiny”。这样做的结果是 Capstone 仅了解足以确定每条指令长度的指令集,并提供有关位置相关指令的一些详细信息。这个想法是只支持我们的 Relocator 实现所需要的,因为它们在 Interceptor 和 Stalker 背后完成了大部分繁重的工作。 + +虽然我不建议使用这些构建选项,除非您真的需要那么小的足迹,但了解什么是可能的还是很好的。我们还提供其他不太极端的选项。在我们的 [footprint docs][] 中阅读更多内容。 + +## 多重构建系统的诅咒 + +只要 Frida 存在,一直困扰我的一件事是,构建 Frida 涉及处理多个构建系统。虽然我们当然试图将这种复杂性隐藏在 scripts/makefiles 后面,但我们不可避免地会有不快乐的用户,他们发现自己试图弄清楚为什么 Frida 没有为他们构建。 + +有些人也有兴趣为稍微不同的 libc、工具链或其他什么交叉编译 Frida。他们甚至可能希望添加对我们尚不支持的操作系统的支持。当我们意识到他们需要处理四个不同的构建系统时,我们很可能会让他们失去动力:Meson、autotools、自定义 Perl 脚本 (OpenSSL) 和 GN (V8)。 + +由于我们是 Meson 的快乐用户,我的目标是“Mesonify all the things!”。在这个版本中,我们终于达到了几乎所有必需的依赖项都使用 Meson 构建的地步。唯一的例外是 V8,但我们希望有一天也能用 Meson 构建它。(来自未来的剧透:Frida 16 将带我们到达那里!) + +## EOF + +还有很多其他令人兴奋的更改,所以一定要查看下面的变更日志。 + +享受吧! + +### 变更日志 + +- 添加对 FreeBSD 的支持。 +- 添加对 ARMv4 的支持,用于检测旧的嵌入式系统。 +- 添加用于减少二进制足迹的构建选项(请参阅 config.mk)。 +- 调整 Capstone 和 GLib 以启用更小的二进制足迹。 +- droidy: 添加 AXML 解码器。感谢 [@meme][]! +- windows: 修复对 libffi-frames 的 dbghelp backtracer 支持。 +- linux: 修复与新 glibc 版本的兼容性。 +- linux: 安装资产时仅使用一个 frida-helper。 +- qnx: 关闭注入器时删除临时文件。 +- core: 取消 agent 拆除时堵塞泄漏。 +- gumjs: 修复返回 *null* 的 RPC 调用的处理。 +- interceptor: 在 x86 上需要时使用“jumbo”-JMP,当无法分配可从“JMP ”到达的内存时。 +- interceptor: 生成可变大小的 x86 NOP 填充。 +- stalker: 改进 arm64 后端的性能,通过应用最近用于优化 x86/64 后端的想法 —— 例如改进的局部性、更好的内联缓存等。 +- stalker: 修复零大小 freeze/thaw() 的处理。 +- stalker: 重做 x86 PLT 排除代码以避免 stalking 期间的重入问题。 +- stalker: 不要求存在 C++ 运行时。 +- arm-writer: 添加 put_bl_reg(), put_call_reg()。 +- arm-writer: 修复 put_call_address*() 中的寄存器破坏。 +- arm64-reader: 公开 disassemble_instruction_at()。 +- arm64-writer: 修复混合宽度文字的处理。 +- arm64-writer: 修复 TBZ/TBNZ 编码。 +- arm64-writer: 修复 32 位系统上的 put_and_reg_reg_imm()。 +- arm64-writer: 添加 put_{ldr,str}_reg_reg_offset_mode()。 +- elf-module: 公开更多细节,添加对离线模式的支持,添加 Vala 绑定。 +- exceptor: 为异常处理程序恢复添加 reset()。 +- gum: 添加大量 GObject Introspection 注释和 API 文档。 +- gum: 添加对作为 Meson 子项目使用的支持。 +- gum: 将 Meson 构建系统移植到 Windows。 +- gum: 消除 GIO 依赖。 +- gum: 添加 diet 模式,允许使用 Interceptor 的“Hello World” C 程序在带有 Thumb 指令的 32 位 ARM 上小至 55K。 +- gum: 添加 CI。感谢 [@meme][]! +- 通过许多可移植性改进改进构建系统。 +- 使用 Meson 构建 elfutils, libiconv, libdwarf, libunwind, openssl, xz。 +- 升级依赖项: capstone, elfutils, libdwarf, libunwind。 +- 发布 Fedora 35 而不是 34 的包。 +- python: 使用 PEP 503 而不是 PyPI xmlrpc。感谢 [@GalaxySnail][]! +- python: 修复 Windows 上对 Python >= 3.10 的支持。 + + +[GObject Introspection]: https://gi.readthedocs.io/en/latest/ +[binding]: https://gi.readthedocs.io/en/latest/users.html +[auto-generated reference docs]: https://gitlab.gnome.org/GNOME/gi-docgen/ +[CI]: https://github.com/frida/frida-gum/actions +[subprojects]: https://mesonbuild.com/Subprojects.html +[devkit binaries]: https://github.com/frida/frida/releases +[Gum Hello World]: https://github.com/oleavr/hello-gum +[footprint docs]: /docs/footprint/ +[@meme]: https://github.com/meme +[@GalaxySnail]: https://github.com/GalaxySnail diff --git a/_i18n/cn/_posts/2022-02-03-frida-15-1-16-released.markdown b/_i18n/cn/_posts/2022-02-03-frida-15-1-16-released.markdown new file mode 100644 index 00000000..592283b2 --- /dev/null +++ b/_i18n/cn/_posts/2022-02-03-frida-15-1-16-released.markdown @@ -0,0 +1,21 @@ +--- +layout: news_item +title: 'Frida 15.1.16 发布' +date: 2022-02-03 22:33:58 +0100 +author: oleavr +version: 15.1.16 +categories: [release] +--- + +这次我们为您带来了两个错误修复和一个新功能,正好赶上周末。 + +Gum 过去依赖于 GIO,但在上一版本中删除了该依赖项。该更改的不幸结果是 agent 和 gadget 不再拆除 GIO,因为它们依赖于 Gum 的拆除代码来执行此操作。这意味着我们留下了线程,这绝不是一件好事。所以这是第一个错误修复。 + +同样在上一版本中,在我们的 Python 绑定中,setup.py 经历了一些重大更改。我们改进了 .egg 下载逻辑,但设法破坏了本地 .egg 逻辑。这是第二个错误修复。 + +关于新功能。对于那些使用 Gum 的 JavaScript 绑定 GumJS 的人,我们现在支持 `console.count()` 和 `console.countReset()`。这些由浏览器和 Node.js 实现,可以轻松计算给定标签被看到的次数。感谢 [@yotamN][] 的这一美好贡献。 + +享受吧! + + +[@yotamN]: https://github.com/yotamN diff --git a/_i18n/cn/_posts/2022-02-10-frida-15-1-17-released.markdown b/_i18n/cn/_posts/2022-02-10-frida-15-1-17-released.markdown new file mode 100644 index 00000000..5008cebd --- /dev/null +++ b/_i18n/cn/_posts/2022-02-10-frida-15-1-17-released.markdown @@ -0,0 +1,36 @@ +--- +layout: news_item +title: 'Frida 15.1.17 发布' +date: 2022-02-10 22:51:56 +0100 +author: oleavr +version: 15.1.17 +categories: [release] +--- + +此版本中一个显著的改进是 `Java.backtrace()` 进行了重大改革。它现在是惰性的,速度提高了 10 倍以上。我还改进了它的 [API][],现在被认为是稳定的。 + +在开发 [frida-java-bridge][] 时,我优化了 Env 对象的处理方式,因此如果我们已经为当前线程拥有一个实例,我们可以回收它。 + +其余的好东西都在下面的变更日志中涵盖,所以一定要查看一下。 + +享受吧! + +### 变更日志 + +- 修复 i/macOS、Android 和 QNX 上的 devkits,其中缺少部分 libiconv。 +- 改进 LLVM 工具链上的 devkit 打包。 +- 改进 GLib 中的 diet 模式支持。 +- cmodule: 公开 g_strndup()。 +- cmodule: 公开 Gum 的线程本地存储 API。 +- java: 重做 Java.backtrace() 以使其惰性且速度提高 10 倍以上。 + - 将线程转换和堆栈遍历逻辑移动到 CModule。 + - 返回一个 Backtrace 对象而不是带有帧的数组。 + - 提供一个廉价的 *id* 属性,可用于去重。 + - 访问 *frames* 属性时惰性计算帧。 +- java: 尽可能避免昂贵的 Env 创建。 +- python: 改进索引 URL 处理。感谢 [@GalaxySnail][]! + + +[API]: https://github.com/DefinitelyTyped/DefinitelyTyped/commit/37db50ef28bb33d3dbbd3250107d15213861bbf5 +[frida-java-bridge]: https://github.com/frida/frida-java-bridge +[@GalaxySnail]: https://github.com/GalaxySnail diff --git a/_i18n/cn/_posts/2022-05-04-frida-15-1-18-released.markdown b/_i18n/cn/_posts/2022-05-04-frida-15-1-18-released.markdown new file mode 100644 index 00000000..8a26f18d --- /dev/null +++ b/_i18n/cn/_posts/2022-05-04-frida-15-1-18-released.markdown @@ -0,0 +1,81 @@ +--- +layout: news_item +title: 'Frida 15.1.18 发布' +date: 2022-05-04 19:43:52 +0200 +author: oleavr +version: 15.1.18 +categories: [release] +--- + +这次到处都有很多改进。许多稳定性改进。 + +我继续改进我们的 CI 和构建系统基础设施。QNX 和 MIPS 支持在长期回归后起死回生。由于缺乏 CI 覆盖,这些问题一直未被注意到。我们现在有了 CI 来确保这种情况不再发生。 + +请务必查看下面的变更日志,以全面了解新内容。 + +享受吧! + +### 变更日志 + +- gadget: 修复 Gadget 在等待 resume() 时持有其内部锁并阻塞动态链接器时的死锁。在这种情况下,我们在阻塞时处理 JS MainContext,而网络 I/O 由 Gadget 线程处理。因为 Stalker 可能会在其 class_init() 期间与动态链接器交互,所以我们必须确保它发生在 JS 线程而不是网络 I/O 线程中。 +- exit-monitor: 修复 fork() 未被注意到时的死锁。 +- darwin: 在 Corellium 上运行时跳过伪签名。 +- darwin: 修复与旧版 iOS SDK 的兼容性。 +- darwin-backtracer: 修复循环变量回绕。 +- darwin-mapper: 修复带有链式修复的足迹预算。 +- linux: 修复 Process.modify_thread() 等待逻辑。 +- linux: 在现代 glibc 系统上完全解析 libc。 +- linux: 移除 Linjector 清理延迟。 +- ios: 添加 config.mk 选项以在没有越狱支持代码的情况下构建,以便与例如 TestFlight 兼容。 +- ios: 消除对外部 Mach VM 头文件的依赖。 +- android: 修复 Android/x86 上的 unw_getcontext(),这会导致例如 Thread.backtrace() 期间的堆栈损坏。 +- freebsd: 为方便起见添加 BSDmakefile。 +- freebsd: 提高 gum_clear_cache() 的可靠性。 +- freebsd: 改进程序路径查询 API。 +- freebsd: 当 MAP_32BIT 失败时再试一次。 +- mips: 修复由于缺乏 CI 而未被注意到的长期回归。 +- qnx: 修复由于缺乏 CI 而未被注意到的长期回归。同时改进 QNX 后端。 +- posix: 如果 madvise() 不可用,则使用 posix_madvise()。 +- stalker: 检测 glibc、Android 和 FreeBSD 上的线程退出实现。 +- stalker: 修复 x86 后端中的 Linux clone() 支持。 +- stalker: 修复 JECXZ/JRCXZ x86 指令的回补。 +- stalker: 修复 x86 后端中的两个 *pc* vs *code* 混淆。 +- stalker: 优化 x86 后端中的 Linux 系统调用逻辑。 +- stalker: 将展开支持移至 API 后面。 +- stalker: 简化并修复 x86 展开 Interceptor 逻辑。 +- stalker: 在 arm64 后端实现展开 hook。感谢 [@s1341][]! +- stalker: 添加回补查询支持。 +- stalker: 添加对重新编译块的支持。 +- process: 添加 resolve_module_pointer()。 +- elf-module: 改进 DT_SYMTAB 条目计数检测。 +- elf-module: 跳过具有悬空名称引用的符号。 +- symbolutil-libdwarf: 添加对 DWARF v5 的支持。 +- libdwarf: 向后移植上游修复以处理 DW_FORM_line_strp。 +- dbghelp-backtracer: 提高 32 位 x86 上的可靠性。 +- arm-relocator: 简化 PC 相对 *LDR* 处理。 +- arm-writer: 添加 call_reg_with_arguments*()。 +- arm-writer: 处理带有超过四个参数的 put_call*()。 +- thumb-writer: 处理 put_call*() 堆栈对齐。 +- arm64-writer: 优化 *LDR reg, #0*。 +- x86-relocator: 添加 *input_pc* 以支持离线使用。 +- x86-relocator: 修复 RIP 相对快速路径中的 PC 与输出混淆。 +- x86-relocator: 改进 RIP 偏移修复逻辑。 +- bounds-checker: 回退到匹配的堆 API。 +- gumjs: 修复 CModule 内存分配逻辑。 +- gumjs: 修复带有内部 TinyCC 的 CModule 运行时。 +- gumjs: 将构建时 Python 要求降低到 3.7。 +- heap-api: 改进非 Windows 操作系统上的 libc 检测。 +- heap-api: 在列表中包含静态 MSVC CRT API。 +- gum: 改进 Meson 构建系统以支持 MSVC。 +- gum: 改进 Gum vapis 以公开更多 API。 +- core: 修复某些语言环境下的构建失败,方法是在从 modulate.py 调用工具时关闭本地化。 +- python: 修复由 get_max_argument_count() 中不正确的引用计数引起的稳定性问题。 +- python: 修复 get_index_url_from_pip()。感谢 [@X5tar][]! +- python: 修复 Python < 3.6 上的索引 URL 检索。感谢 [@serfend][]! +- node: 发布 Electron 18 而不是 16 的预构建。 +- ci: 添加 Linux/MIPS, FreeBSD/x86_64, FreeBSD/arm64, 和 QNX/armeabi。对于 Gum,还添加 Windows/x86_64, macOS/x86_64, Linux/x86, Linux/x86_64, iOS/arm64, Android/x86, Android/arm, 和 Android/arm64。 + + +[@s1341]: https://github.com/s1341 +[@X5tar]: https://github.com/X5tar +[@serfend]: https://github.com/serfend diff --git a/_i18n/cn/_posts/2022-05-04-frida-15-1-19-released.markdown b/_i18n/cn/_posts/2022-05-04-frida-15-1-19-released.markdown new file mode 100644 index 00000000..643dc8aa --- /dev/null +++ b/_i18n/cn/_posts/2022-05-04-frida-15-1-19-released.markdown @@ -0,0 +1,17 @@ +--- +layout: news_item +title: 'Frida 15.1.19 发布' +date: 2022-05-04 23:16:38 +0200 +author: oleavr +version: 15.1.19 +categories: [release] +--- + +事实证明,15.1.18 有一个发布自动化错误,导致上传了陈旧的 Python 绑定工件。 + +为了让这个版本更快乐一点,我还为 x86/64 增加了一个 Stalker 改进,其中 *clone3* 系统调用现在也得到了 [handled][]。这是由 Stalker 的测试套件在某些系统上捕获的。 + +享受吧! + + +[handled]: https://github.com/frida/frida-gum/commit/eb7621136af07d3db38a68effafa37087d23b8d4 diff --git a/_i18n/cn/_posts/2022-05-06-frida-15-1-20-released.markdown b/_i18n/cn/_posts/2022-05-06-frida-15-1-20-released.markdown new file mode 100644 index 00000000..3b543e93 --- /dev/null +++ b/_i18n/cn/_posts/2022-05-06-frida-15-1-20-released.markdown @@ -0,0 +1,18 @@ +--- +layout: news_item +title: 'Frida 15.1.20 发布' +date: 2022-05-06 00:27:46 +0200 +author: oleavr +version: 15.1.20 +categories: [release] +--- + +我们发现 15.1.10 破坏了 Android/x86_64 上 frida-java-bridge 中的内联 hook。此版本 [fixes][] 了它。 + +这次我们还将 Node.js 预构建版本从 v17 迁移到 v18。(唉,应该将 frida-node 移植到 [Node-API][],这样我们就可以停止这种疯狂了!) + +享受吧! + + +[fixes]: https://github.com/frida/frida-java-bridge/commit/32f2faa7064eee629bc03fafcac90cfbeb4e5018 +[Node-API]: https://nodejs.org/api/n-api.html diff --git a/_i18n/cn/_posts/2022-05-06-frida-15-1-21-released.markdown b/_i18n/cn/_posts/2022-05-06-frida-15-1-21-released.markdown new file mode 100644 index 00000000..5d993896 --- /dev/null +++ b/_i18n/cn/_posts/2022-05-06-frida-15-1-21-released.markdown @@ -0,0 +1,16 @@ +--- +layout: news_item +title: 'Frida 15.1.21 发布' +date: 2022-05-06 20:25:35 +0200 +author: oleavr +version: 15.1.21 +categories: [release] +--- + +这次唯一的更改是针对 i/macOS 的一个重要错误修复,其中导入并不总是能被解析。感谢 [@mrmacete][] 的这个 [fix][]。 + +享受吧! + + +[@mrmacete]: https://twitter.com/bezjaje +[fix]: https://github.com/frida/frida-gum/commit/5582d1f922368ccf0f8c7576be1717e14d64ced0 diff --git a/_i18n/cn/_posts/2022-05-09-frida-15-1-22-released.markdown b/_i18n/cn/_posts/2022-05-09-frida-15-1-22-released.markdown new file mode 100644 index 00000000..d928798f --- /dev/null +++ b/_i18n/cn/_posts/2022-05-09-frida-15-1-22-released.markdown @@ -0,0 +1,18 @@ +--- +layout: news_item +title: 'Frida 15.1.22 发布' +date: 2022-05-09 11:20:25 +0200 +author: oleavr +version: 15.1.22 +categories: [release] +--- + +事实证明,Gum 在 15.1.15 中经历的大手术引入了一些错误处理回归。Vala 代码抛出的错误与 frida-core 实际预期的错误不匹配,这导致进程崩溃,而不是将友好的错误冒泡到例如 Python 绑定。这现在终于得到了解决。我希望我们能早点注意到它,但是 —— 我们显然在这个领域缺乏测试覆盖率。 + +除了错误处理修复之外,我们还包括一个针对增量构建问题的构建系统 [fix][]。感谢 [Londek][] 的这一美好贡献。 + +享受吧! + + +[fix]: https://github.com/frida/frida/commit/6a717d636a87c501327c77a70080b495556c8d25 +[Londek]: https://github.com/londek diff --git a/_i18n/cn/_posts/2022-05-30-frida-15-1-23-released.markdown b/_i18n/cn/_posts/2022-05-30-frida-15-1-23-released.markdown new file mode 100644 index 00000000..8f7587ed --- /dev/null +++ b/_i18n/cn/_posts/2022-05-30-frida-15-1-23-released.markdown @@ -0,0 +1,49 @@ +--- +layout: news_item +title: 'Frida 15.1.23 发布' +date: 2022-05-30 10:27:57 +0200 +author: oleavr +version: 15.1.23 +categories: [release] +--- + +此版本的主题是操作系统支持,我们修复了 Android 12 上的一些粗糙边缘,并引入了对 Android 13 的初步支持。在开发 frida-java-bridge 时,我还发现自己在 JVM 特定的后端中需要一些 JVMTI 代码。这些位现在已共享,JVM 特定的代码状况更好,对 JDK 17 提供了全新的支持。 + +我们还在几个地方提高了稳定性,并使安全地使用 CodeWriter API 变得更加容易。可移植性也得到了改善,我们基于 QuickJS 的 JavaScript 运行时终于可以在从小端构建机器为大端交叉编译时工作,反之亦然。 + +要了解更多信息,请务必查看下面的变更日志。 + +享受吧! + +### 变更日志 + +- linux: 处理 ptrace() 期间的虚假信号。 +- android: 为 Android 12+ 上的 system_server 添加缺失的 SELinux 规则。 +- android: 修复真实设备上的 Android 13 检测。 +- android: 处理 Android 13 中的新链接器内部结构。 +- java: 改进 Java.enumerateMethods() 错误消息。感谢 [@jpstotz][]! +- java: (android) 处理内联的 GetOatQuickMethodHeader()。 +- java: (android) 改进对非 Google Android 12+ ROM 的支持。 +- java: (android) 修复 Android >= 12 上的 Java.choose()。 +- java: (android) 添加对 Android 13 的支持。 +- java: (android) 修复 x64 重新编译逻辑中的 *threadReg* 破坏。 +- java: (android) 解释为什么 Java.deoptimizeBootImage() 不可用。 +- java: (android) 通过 *api.jvmti* 公开 JVMTI。 +- java: (android) 改进有关 OS 功能的错误消息。 +- java: (jvm) 添加对 JDK 17 的基本支持。 +- java: (jvm) 为 thread_from_jni_environment() 添加回退。 +- java: (jvm) 修复 withJvmThread() 序言/结语逻辑中的 UAF。 +- java: (jvm) 改进 *InstanceKlass* 偏移检测。 +- code-writer: 添加 *flush_on_destroy* 选项。 +- gumjs: 禁用 CodeWriter *flush_on_destroy* 选项。通过这种方式,writer 更安全,因为一旦它们被垃圾回收,它们就不会再写入内存。在那时,目标内存可能不再可写,或者可能由其他代码拥有。 +- gumjs: 需要时嵌入字节交换的 QuickJS 字节码。这意味着 GumJS 可以跨端序交叉编译。 +- gumjs: 修复 Instruction 复制逻辑中的双重释放。 +- gumjs: 修复 Relocator 指令访问器。 +- gumjs: 在 reset() 和 dispose() 上刷新 CodeWriter。 +- gumjs: 改进 NativePointer#strip() 以支持 ARM TBI。 +- gumjs: 在零复制模式下使 Instruction 包装器更安全。 +- gumjs: 堵塞 QuickJS 运行时中的 Relocator 泄漏。 +- quickjs: 修复对字节交换输出的支持。还将 QuickJS 升级到具有 Unicode 14 更新的最新上游版本。 + + +[@jpstotz]: https://github.com/jpstotz diff --git a/_i18n/cn/_posts/2022-06-03-frida-15-1-24-released.markdown b/_i18n/cn/_posts/2022-06-03-frida-15-1-24-released.markdown new file mode 100644 index 00000000..d1da8ed1 --- /dev/null +++ b/_i18n/cn/_posts/2022-06-03-frida-15-1-24-released.markdown @@ -0,0 +1,15 @@ +--- +layout: news_item +title: 'Frida 15.1.24 发布' +date: 2022-06-03 22:01:41 +0200 +author: oleavr +version: 15.1.24 +categories: [release] +--- + +这次只有一个更改,但对于那些在 Android 上使用 Frida 的人来说,这是一个重要的更改:我们的 Java 方法 hook 实现在某些情况下会崩溃,因为我们会选择一个与生成的代码冲突的暂存寄存器。这现在已 [fixed][]。 + +享受吧! + + +[fixed]: https://github.com/frida/frida-java-bridge/commit/d4d2a42ef2f370487a88d108e966de30f2a48322 diff --git a/_i18n/cn/_posts/2022-06-18-frida-15-1-25-released.markdown b/_i18n/cn/_posts/2022-06-18-frida-15-1-25-released.markdown new file mode 100644 index 00000000..d157234d --- /dev/null +++ b/_i18n/cn/_posts/2022-06-18-frida-15-1-25-released.markdown @@ -0,0 +1,61 @@ +--- +layout: news_item +title: 'Frida 15.1.25 发布' +date: 2022-06-18 09:57:10 +0200 +author: oleavr +version: 15.1.25 +categories: [release] +--- + +此版本中有相当多令人兴奋的位。让我们直接潜入。 + +## FPU/向量寄存器访问 + +对于在 32 位和 64 位 ARM 上使用 Frida 的人来说,这是一个好消息。到目前为止,我们只公开了 CPU 的整数寄存器,但从此版本开始,FPU/向量寄存器也可用了!🎉 + +对于 32 位 ARM,这意味着 *q0* 到 *q15*,*d0* 到 *d31*,以及 *s0* 到 *s31*。至于 64 位 ARM,它们是 *q0* 到 *q31*,*d0* 到 *d31*,以及 *s0* 到 *s31*。如果您从 JavaScript 访问这些,向量属性使用 *ArrayBuffer* 表示,而对于其他属性,我们使用 *number* 类型。 + +## Java.backtrace() + +我们现有的 Java.backtrace() API 现在在返回的 *frames* 中提供了几个新属性,这些属性现在还公开了 *methodFlags* 和 *origin*。 + +## 质量 + +我终于 [plugged][] 了我们 RPC 服务器端代码中的内存泄漏。这是我在 15.1.10 中为 DBus 回复处理在 Vala 编译器的代码生成中实现 [optimization][] 时引入的。感谢 [@rev1si0n][] 报告并帮助追踪此回归! + +## EOF + +此版本中还有一些其他好东西,所以一定要查看下面的变更日志。 + +享受吧! + +### 变更日志 + +- vala: 堵塞服务器端 GDBus 回复处理中的泄漏。这影响了 Frida 中的所有服务器端实现。 +- glib: 禁用对“charset.alias”的支持。这意味着我们不再尝试打开此文件,这可能会在某些系统(如 iOS)上导致沙盒违规。 +- java: (android) 向 Java.backtrace() 帧添加 *methodFlags*。 +- java: (android) 向 Java.backtrace() 帧添加 *origin*。 +- java: (android) 防止 ART 编译被替换的方法。感谢 [@s1341][] 弄清楚这一点! +- cpu-context: 添加 ARM FPU/向量寄存器和 NZCV。 +- cpu-features: 添加 VFPD32 标志和检测逻辑。 +- stalker: 修复 arm 后端中的 VFP D32 检测。 +- gumjs: 将向量寄存器添加到 arm_reg 绑定。 +- x86-writer: 添加 put_fx{save,rstor}_reg_ptr()。 +- arm-writer: 添加无偏移的 load/store 变体。 +- arm-writer: 添加 put_v{push,pop}_range()。 +- arm-writer: 从 put_ands_reg_reg_imm() 中删除 noop 检查。 +- arm-writer: 将 *_registers() 重命名为 *_regs()。 +- arm-writer: 支持带有 Q 寄存器的向量 push/pop。 +- thumb-writer: 添加 put_v{push,pop}_range()。 +- thumb-writer: 支持带有 Q 寄存器的向量 push/pop。 +- arm64-writer: 添加无偏移的 load/store 变体。 +- arm64-writer: 添加 put_mov_{reg_nzcv,nzcv_reg}()。 +- libc-shim: 支持 Linux/ARM 上的旧系统头文件。 +- node: 升级依赖项。 +- node: 发布 Electron 19 而不是 18 的预构建。 + + +[plugged]: https://github.com/frida/vala/commit/d07b689485b3c79116a569696d36ad7c0e299c02 +[optimization]: https://github.com/frida/vala/commit/74a66f908957f9c141e4b50c915a2968721e267a +[@rev1si0n]: https://github.com/rev1si0n +[@s1341]: https://github.com/s1341 diff --git a/_i18n/cn/_posts/2022-06-20-frida-15-1-26-released.markdown b/_i18n/cn/_posts/2022-06-20-frida-15-1-26-released.markdown new file mode 100644 index 00000000..07e80a34 --- /dev/null +++ b/_i18n/cn/_posts/2022-06-20-frida-15-1-26-released.markdown @@ -0,0 +1,18 @@ +--- +layout: news_item +title: 'Frida 15.1.26 发布' +date: 2022-06-20 07:44:22 +0200 +author: oleavr +version: 15.1.26 +categories: [release] +--- + +这次只有一个更改,与上一版本中的 [this][] 修复有关: + +> - java: (android) 防止 ART 编译被替换的方法 + +好吧,事实证明 *kAccCompileDontBother* 常量是不正确的。此版本 [fixes][] 了它。(来自未来的剧透:它没有。) + + +[this]: https://github.com/frida/frida-java-bridge/commit/e9a24559e967dac39edf6f74db37b1287063010c +[fixes]: https://github.com/frida/frida-java-bridge/commit/13451378145e10de880dbc2dfb4fc241e6629959 diff --git a/_i18n/cn/_posts/2022-06-20-frida-15-1-27-released.markdown b/_i18n/cn/_posts/2022-06-20-frida-15-1-27-released.markdown new file mode 100644 index 00000000..ac2a4a63 --- /dev/null +++ b/_i18n/cn/_posts/2022-06-20-frida-15-1-27-released.markdown @@ -0,0 +1,18 @@ +--- +layout: news_item +title: 'Frida 15.1.27 发布' +date: 2022-06-20 11:20:27 +0200 +author: oleavr +version: 15.1.27 +categories: [release] +--- + +看来我今天早上应该多喝点咖啡,所以这是另一个版本,实际上修复了 15.1.25 中 [this][] 损坏的修复: + +> - java: (android) 防止 ART 编译被替换的方法 + +事实证明,*kAccCompileDontBother* 常量在 Android 8.1 中发生了变化。它在 7.0 之前也不存在。哎呀!此版本 [fixes][] 了它,这次是真的 😊 + + +[this]: https://github.com/frida/frida-java-bridge/commit/e9a24559e967dac39edf6f74db37b1287063010c +[fixes]: https://github.com/frida/frida-java-bridge/commit/cdc3d3638c735d008283bfe177cffdd59b0e62c3 diff --git a/_i18n/cn/_posts/2022-07-06-frida-15-1-28-released.markdown b/_i18n/cn/_posts/2022-07-06-frida-15-1-28-released.markdown new file mode 100644 index 00000000..a53c12a9 --- /dev/null +++ b/_i18n/cn/_posts/2022-07-06-frida-15-1-28-released.markdown @@ -0,0 +1,118 @@ +--- +layout: news_item +title: 'Frida 15.1.28 发布' +date: 2022-07-06 17:52:24 +0200 +author: oleavr +version: 15.1.28 +categories: [release] +--- + +此版本中有几个令人兴奋的新事物。 + +## File API + +那些使用我们的 JavaScript File API 的人可能已经注意到它支持写入给定文件,但无法从中读取。现在支持此功能。 + +例如,要将文本文件的每一行作为字符串读取: + +{% highlight js %} +const f = new File('/etc/passwd', 'r'); +let line; +while ((line = f.readLine()) !== '') { + console.log(`Read line: ${line.trimEnd()}`); +} +{% endhighlight %} + +(请注意,这假设文本文件是 UTF-8 编码的。目前不支持其他编码。) + +您还可以读取某个偏移量处的特定字节数: + +{% highlight js %} +const f = new File('/var/run/utmp', 'rb'); +f.seek(0x2c); +const data = f.readBytes(3); +const str = f.readText(3); +{% endhighlight %} + +也可以省略参数以读取文件的其余部分。但是,如果您只想一次性读取文本文件,有一种更简单的方法: + +{% highlight js %} +const text = File.readAllText('/etc/passwd'); +{% endhighlight %} + +读取二进制文件同样容易: + +{% highlight js %} +const bytes = File.readAllBytes('/var/run/utmp'); +{% endhighlight %} + +(其中 *bytes* 是一个 ArrayBuffer。) + +有时您可能还想将字符串转储到文本文件中: + +{% highlight js %} +File.writeAllText('/tmp/secret.txt', 'so secret'); +{% endhighlight %} + +或者转储一个 ArrayBuffer: + +{% highlight js %} +const data = args[0].readByteArray(256); +File.writeAllBytes('/tmp/mystery.bin', data); +{% endhighlight %} + +回到前面的示例,seek() 也支持相对偏移量: + +{% highlight js %} +f.seek(7, File.SEEK_CUR); +f.seek(-3, File.SEEK_END); +{% endhighlight %} + +检索当前文件偏移量同样容易: + +{% highlight js %} +const offset = f.tell(); +{% endhighlight %} + +## Checksum API + +这次添加的另一个 JavaScript API 是用于计算校验和的。虽然这可以完全在“用户空间”的 JavaScript 中实现,但我们相当便宜地获得了它,因为 Frida 依赖于 GLib,并且它已经开箱即用地提供了 [Checksum API][]。我们需要做的就是公开它。 + +综上所述,这意味着我们可以读取文件并计算其 SHA-256: + +{% highlight js %} +const utmp = File.readAllBytes('/var/run/utmp'); +const str = Checksum.compute('sha256', utmp); +{% endhighlight %} + +或者,为了更多的控制: + +{% highlight js %} +const checksum = new Checksum('sha256'); +checksum.update(File.readAllText('/etc/hosts')); +checksum.update(File.readAllBytes('/var/run/utmp')); +console.log('Result:', checksum.getString()); +console.log(hexdump(checksum.getDigest(), { ansi: true })); +{% endhighlight %} + +(您可以从我们的 [TypeScript bindings][] 了解有关此 API 的更多信息。) + +## EOF + +此版本中还有其他一些好东西,所以一定要查看下面的变更日志。 + +享受吧! + +### 变更日志 + +- gumjs: 扩展 *File* API。 +- gumjs: 添加 *Checksum* API。 +- gumjs: 修复从根目录相对 ESM 导入的处理。 +- glib: (win32) 为普通文件添加 Input/OutputStream 支持。 +- build: 在 Windows 上使 XP SDK 可选。 +- node: 目标 Electron 19.0.0 而不是 19.0.0-alpha.1。 +- node: 定义 *openssl_fips* 以解决 node-gyp 问题。 + + +[Checksum API]: https://docs.gtk.org/glib/struct.Checksum.html +[TypeScript bindings]: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/edfba0b718c853dea18c23e2f4b5dd9b4c17dd6d/types/frida-gum/index.d.ts#L2500-L2559 diff --git a/_i18n/cn/_posts/2022-07-21-frida-15-2-0-released.markdown b/_i18n/cn/_posts/2022-07-21-frida-15-2-0-released.markdown new file mode 100644 index 00000000..035ec65e --- /dev/null +++ b/_i18n/cn/_posts/2022-07-21-frida-15-2-0-released.markdown @@ -0,0 +1,300 @@ +--- +layout: news_item +title: 'Frida 15.2.0 发布' +date: 2022-07-21 00:02:32 +0200 +author: oleavr +version: 15.2.0 +categories: [release] +--- + +对此感到非常兴奋。多年来我一直想做的是简化 Frida 的 JavaScript 开发人员体验。作为一名开发人员,我可能从一个非常简单的代理开始,但随着它的增长,我开始感到痛苦。 + +早期我可能想将代理拆分为多个文件。我也可能想使用 npm 中的一些现成包,例如 [frida-remote-stream][]。稍后我会想要代码补全、内联文档、类型检查等,所以我将代理迁移到 TypeScript 并启动 VS Code。 + +由于我们一直在利用现有的令人惊叹的前端 Web 工具,我们已经拥有了所有的拼图。我们可以使用像 [Rollup][] 这样的打包器将我们的源文件合并为一个 .js,我们可以使用 [@frida/rollup-plugin-node-polyfills][] 与 npm 包进行互操作,我们可以插入 [@rollup/plugin-typescript][] 以获得 TypeScript 支持。 + +但这需要反复设置大量的管道,所以我最终创建了 [frida-compile][] 作为一个简单的工具,它可以为您完成管道工作,并针对 Frida 上下文进行了优化配置默认值。不过,这仍然需要一些样板文件,例如 package.json, tsconfig.json 等。 + +为了解决这个问题,我发布了 [frida-agent-example][],这是一个可以克隆并用作起点的 repo。这仍然有点摩擦,所以后来 frida-tools 获得了一个名为 frida-create 的新 CLI 工具。无论如何,即使有了所有这些,我们仍然要求用户安装 Node.js 并处理 npm,并且可能还会对那里的 .json 文件感到困惑。 + +然后我突然想到了。如果我们能够使用 frida-compile 将 frida-compile 编译成一个独立的 .js,我们可以在 Frida 的系统会话上运行它,那会怎么样?系统会话是一个有点晦涩的功能,您可以在托管 frida-core 的进程内加载脚本。例如,如果您正在使用我们的 Python 绑定,该进程将是 Python 解释器。 + +一旦我们能够在 GumJS 中运行该 frida-compile 代理,我们就可以与它通信并将其转化为 API。然后,此 API 可以由语言绑定公开,并且 frida-tools 可以使用它来为用户提供不需要安装 Node.js/npm 的 frida-compile CLI 工具。如果用户要求加载具有 .ts 扩展名的脚本,诸如我们的 REPL 之类的工具也可以无缝使用此 API。 + +所有这些正是我们所做的!🥳 + +## build() + +这是从 Python 使用它有多容易: + +{% highlight python %} +import frida + +compiler = frida.Compiler() +bundle = compiler.build("agent.ts") +{% endhighlight %} + +*bundle* 变量是一个字符串,可以传递给 create_script(),或写入文件。 + +运行该示例,我们可能会看到类似以下内容: + +{% highlight bash %} +Traceback (most recent call last): + File "/home/oleavr/src/explore.py", line 4, in + bundle = compiler.build("agent.ts") + File "/home/oleavr/.local/lib/python3.10/site-packages/frida/core.py", line 76, in wrapper + return f(*args, **kwargs) + File "/home/oleavr/.local/lib/python3.10/site-packages/frida/core.py", line 1150, in build + return self._impl.build(entrypoint, **kwargs) +frida.NotSupportedError: compilation failed +{% endhighlight %} + +这让我们想知道 *为什么* 它失败了,所以让我们为 *diagnostics* 信号添加一个处理程序: + +{% highlight python %} +import frida + +def on_diagnostics(diag): + print("on_diagnostics:", diag) + +compiler = frida.Compiler() +compiler.on("diagnostics", on_diagnostics) +bundle = compiler.build("agent.ts") +{% endhighlight %} + +突然之间一切都变得有意义了: + +{% highlight bash %} +on_diagnostics: [{'category': 'error', 'code': 6053, + 'text': "File '/home/oleavr/src/agent.ts' not " + "found.\n The file is in the program " + "because:\n Root file specified for" + " compilation"}] +… +{% endhighlight %} + +我们忘了实际创建文件!好的,让我们创建 *agent.ts*: + +{% highlight js %} +console.log("Hello from Frida:", Frida.version); +{% endhighlight %} + +让我们也将该脚本写入文件: + +{% highlight python %} +import frida + +def on_diagnostics(diag): + print("on_diagnostics:", diag) + +compiler = frida.Compiler() +compiler.on("diagnostics", on_diagnostics) +bundle = compiler.build("agent.ts") +with open("_agent.js", "w", newline="\n") as f: + f.write(bundle) +{% endhighlight %} + +如果我们现在运行它,我们应该有一个准备好的 _agent.js: + +{% highlight bash %} +$ cat _agent.js +📦 +175 /explore.js.map +39 /explore.js +✄ +{"version":3,"file":"explore.js","sourceRoot":"/home/oleavr/src/","sources":["explore.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,SAAS,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC"} +✄ +console.log(`Hello ${Frida.version}!`); +{% endhighlight %} + +这种看起来很奇怪的格式是 GumJS 允许我们选择加入新的 ECMAScript 模块 (ESM) 格式的方式,其中代码被限制在其所属的模块中,而不是在全局范围内进行评估。这也意味着我们可以加载导入/导出值的多个模块。.map 文件是可选的,可以省略,但如果保留,它们允许 GumJS 将生成的 JavaScript 行号映射回堆栈跟踪中的 TypeScript。 + +无论如何,让我们试用一下 _agent.js: + +{% highlight bash %} +$ frida -p 0 -l _agent.js + ____ + / _ | Frida 15.2.0 - A world-class dynamic instrumentation toolkit + | (_| | + > _ | Commands: + /_/ |_| help -> Displays the help system + . . . . object? -> Display information about 'object' + . . . . exit/quit -> Exit + . . . . + . . . . More info at https://frida.re/docs/home/ + . . . . + . . . . Connected to Local System (id=local) +Attaching... +Hello 15.2.0! +[Local::SystemSession ]-> +{% endhighlight %} + +它有效!现在让我们尝试重构它以将代码拆分为两个文件: + +### agent.ts + +{% highlight typescript %} +import { log } from "./log.js"; + +log("Hello from Frida:", Frida.version); +{% endhighlight %} + +### log.ts + +{% highlight typescript %} +export function log(...args: any[]) { + console.log(...args); +} +{% endhighlight %} + +如果我们现在再次运行我们的示例编译器脚本,它应该生成一个看起来稍微有趣一点的 _agent.js: + +{% highlight bash %} +📦 +204 /agent.js.map +72 /agent.js +199 /log.js.map +58 /log.js +✄ +{"version":3,"file":"agent.js","sourceRoot":"/home/oleavr/src/","sources":["agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,GAAG,CAAC,mBAAmB,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC"} +✄ +import { log } from "./log.js"; +log("Hello from Frida:", Frida.version); +✄ +{"version":3,"file":"log.js","sourceRoot":"/home/oleavr/src/","sources":["log.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,GAAG,CAAC,GAAG,IAAW;IAC9B,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;AACzB,CAAC"} +✄ +export function log(...args) { + console.log(...args); +} +{% endhighlight %} + +将其加载到 REPL 中应该会产生与以前完全相同的结果。 + +## watch() + +让我们将我们的玩具编译器变成一个工具,它可以加载编译后的脚本,并在磁盘上的源文件更改时重新编译: + +{% highlight python %} +import frida +import sys + +session = frida.attach(0) +script = None + +def on_output(bundle): + global script + if script is not None: + print("Unloading old bundle...") + script.unload() + script = None + print("Loading bundle...") + script = session.create_script(bundle) + script.on("message", on_message) + script.load() + +def on_diagnostics(diag): + print("on_diagnostics:", diag) + +def on_message(message, data): + print("on_message:", message) + +compiler = frida.Compiler() +compiler.on("output", on_output) +compiler.on("diagnostics", on_diagnostics) +compiler.watch("agent.ts") + +sys.stdin.read() +{% endhighlight %} + +我们出发了: + +{% highlight bash %} +$ python3 explore.py +Loading bundle... +Hello from Frida: 15.2.0 +{% endhighlight %} + +如果我们让它继续运行,然后在磁盘上编辑源代码,我们应该会看到一些新的输出: + +{% highlight bash %} +Unloading old bundle... +Loading bundle... +Hello from Frida version: 15.2.0 +{% endhighlight %} + +耶! + +## frida-compile + +我们还可以使用 frida-tools 新的 frida-compile CLI 工具: + +{% highlight bash %} +$ frida-compile agent.ts -o _agent.js +{% endhighlight %} + +它还支持监视模式: + +{% highlight bash %} +$ frida-compile agent.ts -o _agent.js -w +{% endhighlight %} + +## REPL + +我们的 REPL 也由新的 frida.Compiler 提供支持: + +{% highlight bash %} +$ frida -p 0 -l agent.ts + ____ + / _ | Frida 15.2.0 - A world-class dynamic instrumentation toolkit + | (_| | + > _ | Commands: + /_/ |_| help -> Displays the help system + . . . . object? -> Display information about 'object' + . . . . exit/quit -> Exit + . . . . + . . . . More info at https://frida.re/docs/home/ + . . . . + . . . . Connected to Local System (id=local) +Compiled agent.ts (1428 ms) +Hello from Frida version: 15.2.0 +[Local::SystemSession ]-> +{% endhighlight %} + +## 致谢 + +感谢 [@hsorbo][] 进行有趣且富有成效的结对编程会议,我们在那里一起开发 frida.Compiler!🙌 + +## EOF + +此版本中还有很多其他好东西,所以一定要查看下面的变更日志。 + +享受吧! + +### 变更日志 + +- core: 添加 Compiler API。目前仅由 Python 绑定公开,但可从 C/Vala 获得。 +- interceptor: 改进 *replace()* 以支持返回原始值。感谢 [@aviramha][]! +- gumjs: 修复 writer 选项中 *pc* 的类型。 +- gumjs: 修复具有循环依赖项的 V8 ESM 崩溃。 +- gumjs: 处理每个模块具有多个别名的 ESM 包。 +- gumjs: 收紧 *Checksum* 数据参数解析。 +- android: 修复崩溃传递中的空指针解引用。感谢 [@muhzii][]! +- fruity: 使用环境变量查找 usbmuxd。感谢 [@0x3c3e][]! +- ios: 使 Substrate 检测逻辑更具弹性。感谢 [@lemon4ex][]! +- meson: 仅在可用时尝试使用 V8。感谢 [@muhzii][]! +- windows: 添加对不带 V8 构建的支持。 +- devkit: 修复 Windows 上的库依赖提示。感谢 [@nblog][]! + + +[frida-remote-stream]: https://github.com/nowsecure/frida-remote-stream +[Rollup]: https://rollupjs.org/guide/en/ +[@frida/rollup-plugin-node-polyfills]: https://www.npmjs.com/package/@frida/rollup-plugin-node-polyfills +[@rollup/plugin-typescript]: https://www.npmjs.com/package/@rollup/plugin-typescript +[frida-compile]: https://www.npmjs.com/package/frida-compile +[frida-agent-example]: https://github.com/oleavr/frida-agent-example +[@hsorbo]: https://twitter.com/hsorbo +[@aviramha]: https://github.com/aviramha +[@muhzii]: https://github.com/muhzii +[@0x3c3e]: https://github.com/0x3c3e +[@lemon4ex]: https://github.com/lemon4ex +[@nblog]: https://github.com/nblog diff --git a/_i18n/cn/_posts/2022-07-21-frida-15-2-1-released.markdown b/_i18n/cn/_posts/2022-07-21-frida-15-2-1-released.markdown new file mode 100644 index 00000000..6152b7e6 --- /dev/null +++ b/_i18n/cn/_posts/2022-07-21-frida-15-2-1-released.markdown @@ -0,0 +1,16 @@ +--- +layout: news_item +title: 'Frida 15.2.1 发布' +date: 2022-07-21 09:36:52 +0200 +author: oleavr +version: 15.2.1 +categories: [release] +--- + +这次有两个小而重要的错误修复: + +- compiler: 忽略 watch() 期间不相关的更改。 +- darwin: 提高内存范围文件信息的准确性。通过在可用时使用 PROC_PIDREGIONPATHINFO2,以便查询受限于 vnode 支持的映射。感谢 [@i0n1c][] 发现并追踪此长期存在的问题。 + + +[@i0n1c]: https://twitter.com/i0n1c diff --git a/_i18n/cn/_posts/2022-07-21-frida-15-2-2-released.markdown b/_i18n/cn/_posts/2022-07-21-frida-15-2-2-released.markdown new file mode 100644 index 00000000..ac3a2746 --- /dev/null +++ b/_i18n/cn/_posts/2022-07-21-frida-15-2-2-released.markdown @@ -0,0 +1,16 @@ +--- +layout: news_item +title: 'Frida 15.2.2 发布' +date: 2022-07-21 20:01:25 +0200 +author: oleavr +version: 15.2.2 +categories: [release] +--- + +另外两个改进,正好赶上周末: + +- darwin: 始终在本地托管系统会话。通过这种方式,我们避免了将 frida-helper 写入临时文件并生成它只是为了使用系统会话 (PID 0)。 +- darwin: 重做 frida-helper IPC 以避免 Mach 端口。这意味着我们避免了在最近版本的 macOS 上崩溃。感谢合著者 [@hsorbo][] 在这方面富有成效的结对编程! + + +[@hsorbo]: https://twitter.com/hsorbo diff --git a/_i18n/cn/_posts/2022-10-08-frida-16-0-0-released.markdown b/_i18n/cn/_posts/2022-10-08-frida-16-0-0-released.markdown new file mode 100644 index 00000000..c9b5fb7b --- /dev/null +++ b/_i18n/cn/_posts/2022-10-08-frida-16-0-0-released.markdown @@ -0,0 +1,153 @@ +--- +layout: news_item +title: 'Frida 16.0.0 发布' +date: 2022-10-08 02:00:19 +0200 +author: oleavr +version: 16.0.0 +categories: [release] +--- + +希望你们中的一些人正在享受 frida.Compiler!如果您不知道那是什么,请查看 [15.2.0 release notes][]。 + +## 性能 + +早在 15.2.0 中,frida.Compiler 就有一些让我困扰的地方:即使在我的 i9-12900K Linux 工作站上,编译一个微小的“Hello World”也需要几秒钟: + +{% highlight bash %} +$ time frida-compile explore.ts -o _agent.js + +real 0m1.491s +user 0m3.016s +sys 0m0.115s +{% endhighlight %} + +经过大量的 [profiling][] 和疯狂的 yak shaving,我终于达到了这个目标: + +{% highlight bash %} +$ time frida-compile explore.ts -o _agent.js + +real 0m0.325s +user 0m0.244s +sys 0m0.109s +{% endhighlight %} + +这真是天壤之别!这意味着诸如 `frida -l explore.ts` 之类的即时编译用例现在更加流畅。更重要的是,这意味着基于 Frida 的工具可以以这种方式加载用户脚本,而无需让用户忍受几秒钟的启动延迟。 + +## 快照 + +您可能想知道我们是如何使编译器启动得如此之快的。如果您查看底层,您会发现它使用了 TypeScript 编译器。这是在启动时解析和运行的大量代码。此外,加载和处理定义所有涉及类型的 .d.ts 文件实际上更加昂贵。 + +我们在 15.2 中实施的第一个优化是简单地使用我们的 V8 运行时(如果可用)。仅此一项就给了我们很好的速度提升。然而,经过一番分析,很明显 V8 意识到一旦我们开始处理 .d.ts 文件,它就在处理繁重的工作负载,这导致它花费大量时间来优化 TypeScript 编译器的代码。 + +这让我想起了一个很久以前注意到的非常酷的 V8 功能:[custom startup snapshots][]。基本上,如果我们能提前预热 TypeScript 编译器并在构建 Frida 时预先创建所有的 .d.ts 源文件,我们就可以在那时对 VM 的状态进行快照,并嵌入生成的启动快照。然后在运行时,我们可以从快照启动并立即运行。 + +作为实现此功能的一部分,我扩展了 GumJS,以便可以将快照与代理的源代码一起传递给 create_script()。还有 snapshot_script(),用于首先创建快照。 + +例如: + +{% highlight python %} +import frida + +session = frida.attach(0) + +snapshot = session.snapshot_script("const example = { magic: 42 };", + warmup_script="true", + runtime="v8") +print("Snapshot created! Size:", len(snapshot)) +{% endhighlight %} + +然后可以将此快照保存到文件,稍后像这样加载: + +{% highlight python %} +script = session.create_script("console.log(JSON.stringify(example));", + snapshot=snapshot, + runtime="v8") +script.load() +{% endhighlight %} + +请注意,快照需要在以后加载它们的相同 OS/架构/V8 版本上创建。 + +## V8 10.x + +另一个令人兴奋的消息是我们已将 V8 升级到 10.x,这意味着我们可以享受最新的 VM 改进和 JavaScript 语言功能。考虑到我们上次升级是在两年多以前,这次绝对是一个可靠的升级。 + +## 多重构建系统的诅咒,第二部分 + +您可能还记得 [15.1.15 release notes][],我们比以往任何时候都更接近达到所有 Frida 都可以使用单个构建系统构建的里程碑。那时剩下的唯一组件是 V8,我们以前使用 Google 的 GN 构建系统来构建它。我很高兴地报告,我们终于达到了那个里程碑。我们现在为 V8 提供了一个全新的 Meson 构建系统。耶! + +## EOF + +还有很多其他令人兴奋的更改,所以一定要查看下面的变更日志。 + +享受吧! + +### 变更日志 + +- compiler: 使用快照减少启动时间。 +- compiler: 升级 frida-compile 和其他依赖项。 +- 添加对 JavaScript VM 快照的支持。这仅由 V8 后端实现,因为 QuickJS 目前不支持此功能。 +- 将调试器 API 从 Session 移动到 Script。这是必要的,因为 V8 的调试器基于每个 Isolate 工作,而我们现在每个 Script 需要一个 Isolate 才能支持快照。 +- server+portal: 修复守护进程父级就绪失败退出。感谢 [@pachoo][]! +- resource-compiler: 添加对压缩的支持。我们将此用于 frida.Compiler 的堆快照。 +- ipc: 增加 UNIX 套接字缓冲区大小以提高吞吐量。 +- meson: 将 frida-payload 提升为公共 API。这允许为 frida-agent 和 frida-gadget 不适用的用例实现自定义有效载荷。 +- windows: 迁移到 Visual Studio 2022。 +- windows: 将工具链/SDK 逻辑移动到使用粒度 SDK。 +- windows: 不依赖 .py 文件关联。 +- darwin: 修复与 macOS 13 和 iOS >= 15.6.1 的兼容性。 +- darwin: 如果存在,使用 Apple 的 libffi-trampolines.dylib,以便我们可以支持 iOS 15 及更高版本。感谢有趣的结对编程会议,[@hsorbo][]! +- fruity: 修复 USBMUXD_SOCKET_ADDRESS 的处理。感谢 [@0x3c3e][]! +- fruity: 放弃对 USBMUXD_SERVER_\* 环境变量的支持。感谢 [@as0ler][]! +- droidy: 改进 ADB 环境变量的处理。感谢 [@0x3c3e][]! +- java: (android) 修复 Android 11 & 12 上的 ClassLinker 偏移检测 (#264)。感谢 [@sh4dowb][]! +- java: (android) 修复 Android 13 上的早期插桩。 +- java: 处理以 *$* 为前缀的方法和字段。感谢 [@eybisi][]! +- android: 迁移到 NDK r25。 +- arm64: 优化内存复制实现。 +- stalker: 确保 EventSink 在拆卸时停止。 +- stalker: 修复分支涉及移位时的 ARM 堆栈破坏。 +- stalker: 处理涉及移位寄存器的 ARM PC 加载。 +- stalker: 应用回补时通知 ARM 观察者。 +- stalker: 收到通知时应用 ARM 回补。 +- stalker: 添加对 switch 块回调的 ARM 支持。 +- arm-reader: 公开 disassemble_instruction_at()。 +- thumb-reader: 公开 disassemble_instruction_at()。 +- memory: 根据当前 V8 语义重新调整 API。 +- gumjs: 将 V8 后端移动到每个脚本一个 Isolate。 +- gumjs: 支持使用环境变量传递 V8 标志:FRIDA_V8_EXTRA_FLAGS。 +- gumjs: 在 Darwin/arm\* 上使用 V8 写保护。 +- gumjs: 添加对动态定义脚本的支持。 +- prof: 支持 Linux/MIPS 上的旧系统头文件。 +- devkit: 改进 UNIX 上的示例编译文档。 +- ci: 将剩余的旧版 CI 迁移到 GitHub Actions。 +- quickjs: 修复模块评估期间出错时的 use-after-free。 +- v8: 升级到最新的 V8 10.x。 +- v8: 添加 Meson 构建系统。 +- usrsctp: 将 Windows 要求降低到 XP,就像我们的其他组件一样。 +- xz: 避免 ANSI 时代的 Windows API。 +- libc-shim: 支持 Linux/MIPS 上的旧系统头文件。 +- glib: 为 MIPS 添加 Linux libc 回退。 +- 添加 config.mk 选项以能够禁用 Android 上的模拟代理,从而允许构建更小的二进制文件。感谢 [@muhzii][]! +- python: 放弃 Python 2 支持,现代化代码,添加文档字符串,类型提示,添加具有现代工具的 CI,以及许多其他好东西。感谢 [@yotamN][]! +- python: 构建 Python wheels 而不是 eggs。感谢 [@oriori1703][]! +- python: 修复 Device.get_bus()。以前的实现调用了不存在的 \_Device.get_bus()。感谢 [@oriori1703][]! +- python: 迁移到稳定的 Python C API。 +- python: 添加对从源代码构建的支持,使用 frida-core devkit。 +- python: 添加对新快照 API 的支持。 +- node: 添加对新快照 API 的支持。 +- node: 修复 Electron v20 兼容性。 + + +[15.2.0 release notes]: {% post_url _i18n/cn/2022-07-21-frida-15-2-0-released %} +[profiling]: https://github.com/frida/frida-core/blob/155328df3420ead34e485f1c4fb7e5b3fe7d71a6/tests/profile-compiler.sh +[custom startup snapshots]: https://v8.dev/blog/custom-startup-snapshots +[15.1.15 release notes]: {% post_url _i18n/cn/2022-02-01-frida-15-1-15-released %} +[@pachoo]: https://github.com/pachoo +[@hsorbo]: https://twitter.com/hsorbo +[@0x3c3e]: https://github.com/0x3c3e +[@as0ler]: https://twitter.com/as0ler +[@sh4dowb]: https://github.com/sh4dowb +[@eybisi]: https://github.com/eybisi +[@muhzii]: https://github.com/muhzii +[@yotamN]: https://github.com/yotamN +[@oriori1703]: https://github.com/oriori1703 diff --git a/_i18n/cn/_posts/2022-10-08-frida-16-0-1-released.markdown b/_i18n/cn/_posts/2022-10-08-frida-16-0-1-released.markdown new file mode 100644 index 00000000..2fa3fd69 --- /dev/null +++ b/_i18n/cn/_posts/2022-10-08-frida-16-0-1-released.markdown @@ -0,0 +1,13 @@ +--- +layout: news_item +title: 'Frida 16.0.1 发布' +date: 2022-10-08 14:59:07 +0200 +author: oleavr +version: 16.0.1 +categories: [release] +--- + +这次有两个小而重要的错误修复: + +- python: 修复计算出的 sdist 版本元数据。 +- python: 假设 macOS 上是非通用 devkit 构建。 diff --git a/_i18n/cn/_posts/2022-10-22-frida-16-0-2-released.markdown b/_i18n/cn/_posts/2022-10-22-frida-16-0-2-released.markdown new file mode 100644 index 00000000..47cf44b0 --- /dev/null +++ b/_i18n/cn/_posts/2022-10-22-frida-16-0-2-released.markdown @@ -0,0 +1,35 @@ +--- +layout: news_item +title: 'Frida 16.0.2 发布' +date: 2022-10-22 01:59:41 +0200 +author: oleavr +version: 16.0.2 +categories: [release] +--- + +今天是星期五!这是一个全新的版本,有很多改进: + +- macOS: 修复在 macOS >= 12 上附加到 arm64e 系统应用程序和服务的支持。 +- i/macOS: 升级对链式修复的支持。 +- iOS: 修复运行 iOS < 14 的 arm64e 设备上的系统会话。 +- system-session: 禁用 Exceptor 和监视器。 + - Exceptor 会干扰宿主进程的信号处理,这是有风险的并且容易发生冲突。 + - 监视器对系统会话并不真正有用。 +- interceptor: 修复 iOS/arm64 上 grafted 模式下使用的蹦床。感谢 [@mrmacete][]! +- compiler: 修复多个平台上的稳定性问题。 +- compiler: 修复 @frida/path shim。 +- compiler: 通过在非 x86/64 Linux 上也使用快照来加速事情。 +- devkit: 修复 Windows, macOS 和 Linux devkits 中的回归。 +- v8: 修复由于我们的 libc-shim 未能实现 V8 依赖的 malloc_usable_size() API 而导致的堆损坏。 +- v8: 修复对使用不同快照的脚本的支持,这由于 V8 以前被配置为跨隔离共享只读空间而被破坏。该选项与多个快照冲突。 +- v8: 改进拆卸。 +- v8: 修复 v8::External API 契约违规。 +- v8: 升级到 10.9.42。 +- zlib: 升级到 1.2.13。 +- gum: 修复基于 ELF 的 OS 的子项目构建。 +- python: 修复已发布的 Linux wheels 的标签。 + - 添加旧版平台标签,以便旧版本的 pip 能够识别它们。 + - 假装一些 wheels 需要 glibc >= 2.17,因为较低版本在某些架构上无法识别。 + + +[@mrmacete]: https://twitter.com/bezjaje diff --git a/_i18n/cn/_posts/2022-11-23-frida-16-0-3-released.markdown b/_i18n/cn/_posts/2022-11-23-frida-16-0-3-released.markdown new file mode 100644 index 00000000..e7ae250f --- /dev/null +++ b/_i18n/cn/_posts/2022-11-23-frida-16-0-3-released.markdown @@ -0,0 +1,106 @@ +--- +layout: news_item +title: 'Frida 16.0.3 发布' +date: 2022-11-23 12:02:32 +0100 +author: oleavr +version: 16.0.3 +categories: [release] +--- + +这次有一些很酷的新东西。让我们直接潜入。 + +## tvOS 和 watchOS + +这次令人兴奋的贡献之一来自 [@tmm1][],他打开了一大堆 pull-request,增加了对 tvOS 的支持。耶!作为落地这些的一部分,我借此机会也增加了对 watchOS 的支持。 + +这也证明是简化构建系统的好时机,摆脱了为支持非 Meson 构建系统(如 autotools)而引入的复杂性。因此,作为此清理的一部分,我们现在为模拟器目标(如 iOS 模拟器、tvOS 模拟器等)提供了单独的二进制文件。以前我们只支持 x86_64 iOS 模拟器,现在 arm64 也被覆盖了。 + +## macOS 13 和 iOS 16 + +本周早些时候,[@hsorbo][] 和我进行了一些有趣且富有成效的结对编程,我们解决了 Apple 最新操作系统中的动态链接器更改。那些在 i/macOS 上使用 Frida 的人可能已经注意到 spawn() 在 macOS 13 和 iOS 16 上停止工作。 + +这很有趣。事实证明,文件系统上的 dyld 二进制文件现在会在 [dyld_shared_cache][] 中查找与其自身具有相同 UUID 的 dyld,如果找到,则将执行向量到那里。解释为什么这破坏了 Frida 的 spawn() 功能需要一点背景知识,所以请忍受我一下。 + +当您调用 attach() 时,Frida 做的一部分工作是注入其代理(如果尚未这样做)。然而,在执行注入之前,我们会检查进程是否已充分初始化,即 libSystem 是否已初始化。 + +如果情况并非如此,例如在 spawn() 之后,目标在 dyld 的入口点暂停,Frida 基本上会推进主线程的执行,直到它到达 libSystem 准备就绪的点。这通常使用硬件断点来完成。 + +因此,因为新的 dyld 现在链接到 dyld_shared_cache 中的另一个副本,Frida 将断点放置在从文件系统映射的版本中,而不是缓存中的版本中。显然那从未被击中,所以我们在等待这种情况发生时最终会超时。 + +不过 [fix][] 相当简单,所以我们在最后一分钟设法将其挤进了发布中。 + +## 编译器改进 + +frida.Compiler 变得更好了,现在支持通过 tsconfig.json 进行额外配置,以及使用本地 frida-gum 类型定义。 + +## V8 调试器 + +V8 调试器集成因每个脚本拥有一个 V8 Isolate 的举措而被淘汰,这是 V8 快照支持所需的微妙重构。这现在已恢复正常工作。 + +## 依赖项升级 + +这次较重的提升之一显然是依赖项升级,我们的大多数依赖项现在都已升级:从支持 ARMv9.2 的 Capstone 到使用 PCRE2 的最新 GLib 等。 + +迁移到 PCRE2 意味着我们的 Memory.scan() 正则表达式支持刚刚升级,因为 GLib 以前使用的是 PCRE1。不过我们尚未在任何平台上启用 PCRE2 的 JIT,但这将是以后很容易改进的事情。 + +## 交叉发布: frida-tools 12.0.2 + +我们还有一个全新的 frida-tools 版本,感谢 [@tmm1][],它具有一个令人兴奋的新功能。*frida-ls-devices* 工具现在显示更高保真度的设备名称,OS 名称和版本显示在第四列中: + +![frida-ls-devices](/img/ls-devices-12-0-2.png "frida-ls-devices in 12.0.2"){: width="100%" } + +要升级: + +{% highlight bash %} +$ pip3 install -U frida frida-tools +{% endhighlight %} + +## EOF + +此版本中还有一些其他好东西,所以一定要查看下面的变更日志。 + +享受吧! + +### 变更日志 + +- darwin: 添加对 watchOS 和 tvOS 的支持。感谢 [@tmm1][]! +- darwin: 修复 macOS 13 和 iOS 16 上的早期插桩。(感谢在此方面的结对编程,[@hsorbo][]!) +- interceptor: 在 W^X 系统(如 iOS)上改变页面时暂停线程。这提高了检测繁忙进程时的稳定性。 +- system-session: 暂时重新启用 Exceptor。 +- compiler: 允许配置 *target*, *lib* 和 *strict*。 +- compiler: 修复对本地 frida-gum 类型定义的支持。 +- compiler: 使用来自 git 的最新 *@types/frida-gum*。 +- ci: 放弃 Node.js 10 的预构建。 +- ci: 发布 Node.js 19 的预构建。 +- ci: 发布 Electron 21 而不是 20 的预构建。 +- unw-backtracer: 提高 32 位 ARM 上的准确性。 +- thread: 向 Gum C API 添加 *suspend()* 和 *resume()*。 +- darwin: 改进链式修复的处理。 +- darwin: 修复 arm64e 上的 Objective-C 符号合成。 +- linux: 检测 Linux 上的 *noxsave*。 +- linux: 改进注入器以处理虚假的 .so 映射。感谢 [@lx866][]! +- module-map: 支持设置了 ptrauth 位的查找。 +- gumjs: 添加 NativeFunction *traps: 'none'* 选项。感谢 [@mrmacete][]! +- gumjs: 防止 File 和 SQLite API 触发 Interceptor。感谢 [@mrmacete][]! +- gumjs: 在执行 V8 作业时持有 Isolate 锁。 +- gumjs: 修复 V8 脚本拆卸时的死锁。 +- gumjs: 修复 V8 调试器看不到加载的脚本。 +- gumjs: 修复 Darwin/arm64e 上的 CModule 外部工具链支持。 +- gumjs: 如果 V8 MAP_JIT 在 Darwin 上失败,则回退。 +- gumjs: 初始化后不要冻结 V8 标志,以避免 Darwin 上强化进程的问题。 +- socket: 升级到 libsoup 3.x 以支持 HTTP/2。 +- devkit: 将 .gir 添加到 frida-core kit。感谢 [@lateralusd][]! +- devkit: 将示例更新为当前的 Device API。 +- python: 在任何地方支持 UNIX 套接字地址。 +- node: 修复 Node.js v19 兼容性。 +- deps: 将大多数依赖项升级到最新最好的版本。 +- build: 重构以摆脱非 Meson 的垃圾。 + + +[@tmm1]: https://twitter.com/tmm1 +[@hsorbo]: https://twitter.com/hsorbo +[dyld_shared_cache]: https://iphonedev.wiki/index.php/Dyld_shared_cache +[fix]: https://github.com/frida/frida-core/commit/73ac5eab9e6912bbd9903270e629e0d0ca773209 +[@lx866]: https://github.com/lx866 +[@mrmacete]: https://twitter.com/bezjaje +[@lateralusd]: https://github.com/lateralusd diff --git a/_i18n/cn/_posts/2022-11-26-frida-16-0-4-released.markdown b/_i18n/cn/_posts/2022-11-26-frida-16-0-4-released.markdown new file mode 100644 index 00000000..0d2b7b82 --- /dev/null +++ b/_i18n/cn/_posts/2022-11-26-frida-16-0-4-released.markdown @@ -0,0 +1,23 @@ +--- +layout: news_item +title: 'Frida 16.0.4 发布' +date: 2022-11-26 01:35:58 +0100 +author: oleavr +version: 16.0.4 +categories: [release] +--- + +这是一个全新的版本,正好赶上周末!🎉 这次有几个关键的稳定性修复。 + +享受吧! + +### 变更日志 + +- gumjs: 修复 V8 JobState 逻辑中的 use-after-free。感谢 [@pancake][] 报告并帮助追踪此问题! +- android: 修复竞争性的 Zygote 和 system_server 检测。感谢与 [@hsorbo][] 进行有趣且富有成效的结对编程! +- submodules: 添加 frida-go。感谢 [@lateralusd][]! + + +[@pancake]: https://twitter.com/trufae +[@hsorbo]: https://twitter.com/hsorbo +[@lateralusd]: https://github.com/lateralusd diff --git a/_i18n/cn/_posts/2022-11-28-frida-16-0-5-released.markdown b/_i18n/cn/_posts/2022-11-28-frida-16-0-5-released.markdown new file mode 100644 index 00000000..7eb1d8b1 --- /dev/null +++ b/_i18n/cn/_posts/2022-11-28-frida-16-0-5-released.markdown @@ -0,0 +1,10 @@ +--- +layout: news_item +title: 'Frida 16.0.5 发布' +date: 2022-11-28 12:09:38 +0100 +author: oleavr +version: 16.0.5 +categories: [release] +--- + +这次是快速的错误修复版本:Android system_server 检测调整现在应该可以在所有系统上可靠地工作。 diff --git a/_i18n/cn/_posts/2022-12-01-frida-16-0-6-released.markdown b/_i18n/cn/_posts/2022-12-01-frida-16-0-6-released.markdown new file mode 100644 index 00000000..c2280681 --- /dev/null +++ b/_i18n/cn/_posts/2022-12-01-frida-16-0-6-released.markdown @@ -0,0 +1,13 @@ +--- +layout: news_item +title: 'Frida 16.0.6 发布' +date: 2022-12-01 01:23:12 +0100 +author: oleavr +version: 16.0.6 +categories: [release] +--- + +事实证明,Frida 16.0.3 中出现了一个严重的稳定性回归,我们用于 Apple OS 的进程外动态链接器最终可能会导致目标进程崩溃。当目标进程是 launchd 时,这尤其具有灾难性,因为它会导致内核恐慌。感谢 [@mrmacete][] 的出色工作,这个令人尴尬的回归现已修复。🎉 享受吧! + + +[@mrmacete]: https://twitter.com/bezjaje diff --git a/_i18n/cn/_posts/2022-12-02-frida-16-0-7-released.markdown b/_i18n/cn/_posts/2022-12-02-frida-16-0-7-released.markdown new file mode 100644 index 00000000..9bdc6262 --- /dev/null +++ b/_i18n/cn/_posts/2022-12-02-frida-16-0-7-released.markdown @@ -0,0 +1,58 @@ +--- +layout: news_item +title: 'Frida 16.0.7 发布' +date: 2022-12-02 22:52:59 +0100 +author: oleavr +version: 16.0.7 +categories: [release] +--- + +这是忙碌的一周。让我们潜入。 + +## 沙盒环境 + +本周 [@hsorbo][] 和我花了一些时间试图让 Frida 在沙盒环境中更好地工作。我们的目标是能够让 Frida 进入 iOS 上的 Apple SpringBoard 进程。但为了让事情变得有趣一点,我们认为我们会从 *imagent* 开始,这是处理 iMessage 协议的守护进程。它在最近的 OS 版本中得到了相当大的强化,Frida 不再能够附加到它。 + +所以我们首先在 macOS 上从这个守护进程开始,只是为了让调试更容易。在 `/System/Library/Sandbox/Profiles/com.apple.imagent.sb` 找到守护进程的沙盒配置文件后,很难错过 syscall 策略。它默认禁止所有 syscall,并仔细启用一些 syscall 组,以及它也需要的一些特定 syscall。 + +然后我们发现 Frida 使用 pipe() syscall 是第一个障碍。这段代码实际上不在 Frida 本身中,而是在 GLib 中,这是 Frida 用于数据结构、跨平台线程原语、事件循环等的优秀库。它使用 pipe() 来实现其事件循环所需的原语。更准确地说,它使用此原语在事件循环的线程阻塞在 poll() 风格的 syscall 中时唤醒它。 + +无论如何,我们注意到 kqueue() 是明确允许的 syscall 组的一部分。鉴于 Apple 的 kqueue() 支持同时轮询文件描述符和 Mach 端口等,它很可能在很多地方都需要,因此被广泛的沙盒配置文件所允许。它也非常适合我们,因为 EVFILT_USER 意味着有一种方法可以唤醒事件循环的线程。不仅如此,它不需要我们花费一个文件描述符。 + +经过大量的咖啡和有趣的结对编程,我们得到了一个 [patch][],它将 GLib 的事件循环切换到支持它的 OS 上的 kqueue()。这让我们遇到了下一个障碍:Frida 正在使用套接字 API 进行文件描述符传递,这是用于检测当前进程子进程的 child-gating 功能的一部分。然而,由于强化的系统服务不太可能被允许做像 fork() 和 execve() 这样的事情,简单地降级这部分功能是可以的。接下来 [tackled][] 了这个问题,然后 boom…… Frida 终于能够附加到 *imagent* 了。🎉 耶! + +接下来我们转移到 iOS 并在那里试了一下。令我们惊讶的是,Frida 一开始就可以附加到 SpringBoard。后来我们尝试了 *notifyd* 和 *profiled*,也可以附加到它们。即使在最新的 iOS 16 上也是如此。但是,仍有工作要做,因为 Frida 尚无法附加到 iOS 上的 *imagent* 和 *WebContent*。不过,这是令人兴奋的进展。 + +## iOS >= 15 上的注入 + +在做所有这些的同时,我们还追踪了 iOS 上的一个崩溃,其中 frida-server 在 iOS >= 15 上注入期间会因 *EXC_GUARD* 而被杀死。这也已修复,正好赶上发布! + +## DebugSymbol API + +另一个令人兴奋的消息是 [@mrmacete][] 改进了我们的 DebugSymbol API,以始终提供完整路径而不仅仅是文件名。这是我们不同平台后端之间长期存在的不一致之处。与此同时,他还公开了 *column*,所以除了行号之外,你还可以获得它。 + +## Interceptor.replace(),但是快 + +最后但并非最不重要的一点是,值得一提的是 Interceptor 中令人兴奋的新改进。对于那些从 C 使用它的人,现在有 *replace_fast()* 来补充 *replace()*。这个新的快速变体发出一个内联 hook,直接向量到你的替换。如果你愿意,你仍然可以调用原始函数,但必须通过 Interceptor 作为可选输出参数提供给你的函数指针来调用它。它也不能与同一目标的 *attach()* 结合使用。不过它要快得多,所以在需要在热代码路径中 hook 函数时绝对值得注意。 + +## EOF + +这就是这次的全部内容。享受吧! + +### 变更日志 + +- darwin: 在强化进程中禁用高级功能。 +- darwin: 移植 GLib 的 MainContext 以使用 kqueue() 而不是 poll()。 +- darwin: 修复 iOS >= 15 上注入期间的 EXC_GUARD。 +- package-server-ios: 将 launchd plist 移植到 iOS 16。 +- gadget: 加载时不要隐藏主线程。 +- debug-symbol: 确保路径是绝对路径并添加列字段。感谢 [@mrmacete][]! +- darwin: 添加 *query_hardened()*。 +- interceptor: 添加 *replace_fast()*。 +- interceptor: 减少每个目标的内存使用量。 + + +[@hsorbo]: https://twitter.com/hsorbo +[patch]: https://github.com/frida/glib/commit/99ec1f987dfbc9b0ab45ac32dd98464cc023cd42 +[tackled]: https://github.com/frida/frida-core/commit/d31a5437c583eee49da9c710d10b8c3aa89710bb +[@mrmacete]: https://twitter.com/bezjaje diff --git a/_i18n/cn/_posts/2022-12-13-frida-16-0-8-released.markdown b/_i18n/cn/_posts/2022-12-13-frida-16-0-8-released.markdown new file mode 100644 index 00000000..6f4bd6be --- /dev/null +++ b/_i18n/cn/_posts/2022-12-13-frida-16-0-8-released.markdown @@ -0,0 +1,36 @@ +--- +layout: news_item +title: 'Frida 16.0.8 发布' +date: 2022-12-13 00:21:38 +0100 +author: oleavr +version: 16.0.8 +categories: [release] +--- + +这次我们专注于完善我们的 macOS 和 iOS 支持。对于那些使用 *spawn()* 和 spawn-gating 进行早期插桩的人来说,情况现在好多了。 + +## i/macOS spawn() 性能 + +此版本中最令人兴奋的变化完全是关于性能的。以前使用 Frida 启动时需要一段时间才能启动的程序现在应该启动得更快。这个长期存在的瓶颈非常糟糕,以至于拥有大量库的应用程序可能会因为 Frida 过度减慢其启动速度而无法启动。 + +## i/macOS 和 SIGPIPE + +接下来我们修复了一个长期存在的可靠性问题。事实证明,我们用于 IPC 的文件描述符没有设置 SO_NOSIGPIPE,因此我们有时可能会遇到 Frida 或目标进程突然终止的情况,而另一方在尝试 write() 时最终会被 SIGPIPE 击中。 + +## 沙盒环境,第二部分 + +上一版本引入了一些大胆的新更改以支持注入强化目标。从那时起,[@hsorbo][] 和我重新深入研究了我们最近的 GLib kqueue() 补丁并修复了一些粗糙的边缘。我们还修复了一个回归,即通过 usbmuxd 附加到强化进程会失败并显示“连接已关闭”。 + +## Linux + +在 Linux 和 Android 方面,你们中的一些人可能已经注意到线程枚举可能会随机失败,尤其是在繁忙的进程中。这个问题现在终于解决了。 + +此外,感谢 [@drosseau][],我们还有一个错误处理改进,应该可以避免在 32-/64-bit 跨架构构建失败时产生一些混淆。 + +## EOF + +这就是这次的全部内容。享受吧! + + +[@hsorbo]: https://twitter.com/hsorbo +[@drosseau]: https://github.com/drosseau diff --git a/_i18n/cn/_posts/2023-02-11-frida-16-0-9-released.markdown b/_i18n/cn/_posts/2023-02-11-frida-16-0-9-released.markdown new file mode 100644 index 00000000..2a13b865 --- /dev/null +++ b/_i18n/cn/_posts/2023-02-11-frida-16-0-9-released.markdown @@ -0,0 +1,42 @@ +--- +layout: news_item +title: 'Frida 16.0.9 发布' +date: 2023-02-11 10:35:06 +0100 +author: oleavr +version: 16.0.9 +categories: [release] +--- + +此版本的主题是改进对越狱 iOS 的支持。我们现在支持 iOS 16.3,包括应用程序列表和启动。还包括改进的 iOS 15 支持,以及针对旧 iOS 版本的一些回归修复。 + +此版本中还有一些其他好东西,所以一定要查看下面的变更日志。 + +享受吧! + +### 变更日志 + +- darwin: 修复 iOS >= 16 上的应用程序列表和启动。感谢与 [@hsorbo][] 进行富有成效的结对编程! +- darwin: 修复现代 dyld 早期插桩期间的崩溃。 +- darwin: 修复早期插桩中的断点冲突,这是 16.0.8 中作为改进 spawn() 性能的一部分引入的回归。感谢 [@mrmacete][]! +- package-server-ios: 强制 xz 压缩。 +- darwin-grafter: 如果需要,创建多个段。感谢 [@mrmacete][]! +- interceptor: 在 Darwin grafted 导入激活时翻转页面保护。感谢 [@mrmacete][]! +- stalker: 修复没有回写的 ARM LDMIA 的处理。 +- darwin: 添加 query_protection()。感谢 [@mrmacete][]! +- darwin: 避免在强化进程中探测内核任务端口。感谢 [@as0ler][]! +- darwin: 修复 Process.modify_thread() 的可靠性。 +- darwin: 修复启用了 tweak 的 iOS 上的 query_hardened()。感谢 [@as0ler][]! +- darwin: 使 Process.modify_thread() 破坏性更小。 +- darwin: 优化 Process.modify_thread()。 +- darwin: 添加 modify_thread()。 +- arm-writer: 添加 put_ldmia_reg_mask_wb()。 +- gumjs: 修复运行时序列化不处理 unicode 的问题。感谢 [@milahu][]! +- python: 向 RPC 调用添加异步变体。感谢 [@yotamN][]! +- python: 为 core 添加特定的信号类型提示。感谢 [@yotamN][]! + + +[@hsorbo]: https://twitter.com/hsorbo +[@mrmacete]: https://twitter.com/bezjaje +[@as0ler]: https://twitter.com/as0ler +[@milahu]: https://github.com/milahu +[@yotamN]: https://github.com/yotamN diff --git a/_i18n/cn/_posts/2023-02-17-frida-16-0-10-released.markdown b/_i18n/cn/_posts/2023-02-17-frida-16-0-10-released.markdown new file mode 100644 index 00000000..6f15f669 --- /dev/null +++ b/_i18n/cn/_posts/2023-02-17-frida-16-0-10-released.markdown @@ -0,0 +1,30 @@ +--- +layout: news_item +title: 'Frida 16.0.10 发布' +date: 2023-02-17 02:05:11 +0100 +author: oleavr +version: 16.0.10 +categories: [release] +--- + +这次我们为您带来了额外的 iOS 15 改进,更好的 frida.Compiler,对 musl libc 的全新支持等等。请务必查看下面的变更日志以获取更多详细信息。 + +享受吧! + +### 变更日志 + +- ios: 修复较低版本 iOS 15 上的 spawn()。感谢 [@as0ler][] 和 [@mrmacete][]! +- ios: 确保 launchd 优先考虑我们的守护进程。感谢 [@getorix][] 调查并建议此修复。 +- compiler: 升级 frida-compile, frida-fs 和 Gum 类型定义。这意味着 frida.Compiler 现在也可以在 linux-arm64 和 linux-x86 上正常工作。 +- linux: 添加对 musl libc 的支持,包括为 x86_64 和 arm64 发布发布资产的 CI。 +- gumjs: 添加 console.debug() 和 console.info()。感谢 [@oriori1703][]! +- gumjs: 修复 node_modules/@types 存在于我们之上时的构建。 +- gumjs: 生成运行时时忽略 tcclib.h 符号。感谢 [@milahu][]! +- build: 改进对共享构建的支持。感谢 [@milahu][]! + + +[@as0ler]: https://twitter.com/as0ler +[@mrmacete]: https://twitter.com/bezjaje +[@getorix]: https://twitter.com/getorix +[@oriori1703]: https://github.com/oriori1703 +[@milahu]: https://github.com/milahu diff --git a/_i18n/cn/_posts/2023-03-10-frida-16-0-11-released.markdown b/_i18n/cn/_posts/2023-03-10-frida-16-0-11-released.markdown new file mode 100644 index 00000000..82318a1f --- /dev/null +++ b/_i18n/cn/_posts/2023-03-10-frida-16-0-11-released.markdown @@ -0,0 +1,19 @@ +--- +layout: news_item +title: 'Frida 16.0.11 发布' +date: 2023-03-10 21:21:55 +0100 +author: oleavr +version: 16.0.11 +categories: [release] +--- + +这次只有几个错误修复: + +- darwin: 修复在非 RWX 系统上暂停线程时的死锁。感谢 [@mrmacete][]! +- darwin: 修复映射零大小段的问题。感谢 [@comex][]! +- linux: 改进 Gum 以支持没有运行时链接器的程序。 +- linux: 修复 Gum.Linux API 的错误传播。 + + +[@mrmacete]: https://twitter.com/bezjaje +[@comex]: https://twitter.com/comex diff --git a/_i18n/cn/_posts/2023-04-14-frida-16-0-12-released.markdown b/_i18n/cn/_posts/2023-04-14-frida-16-0-12-released.markdown new file mode 100644 index 00000000..cf5159b3 --- /dev/null +++ b/_i18n/cn/_posts/2023-04-14-frida-16-0-12-released.markdown @@ -0,0 +1,52 @@ +--- +layout: news_item +title: 'Frida 16.0.12 发布' +date: 2023-04-14 13:25:36 +0200 +author: oleavr +version: 16.0.12 +categories: [release] +--- + +这次有很多好东西。其中之一是我们全新的 Linux 注入器。这很有趣,尤其因为它涉及与 [@hsorbo][] 的大量结对编程。 + +我们对此感到非常兴奋。Frida 现在支持注入 Linux 容器,例如 Flatpak 应用程序。不仅如此,它终于可以将代码注入到没有动态链接器的进程中。 + +另一个整洁的改进是,如果您在 Linux >= 3.17 上运行 Frida,您可能会注意到它不再写出任何临时文件。这是通过 memfd 和重做的注入器信号机制实现的。 + +我们新的 Linux 注入器具有完全异步的设计,没有任何可能导致死锁的危险阻塞操作。它也是第一个支持向注入的有效载荷提供控制通道的注入器。 + +未来的计划是在我们的其他后端中也实现这一点,并使其成为我们跨平台 Injector API 的一部分。这样做的想法是使其更容易实现自定义有效载荷。 + +此版本中还有许多其他好东西,所以一定要查看下面的变更日志。 + +享受吧! + +### 变更日志 + +- linux: 重新设计注入器。感谢与 [@hsorbo][] 进行富有成效的结对编程! +- linux: 修复基于 libc 的模块枚举。 +- linux: 修复 musl 上的 query_libc_name()。 +- linux: 修复 musl 上的模块句柄解析器逻辑。 +- linux: 修复 musl 上的 Module.ensure_initialized()。 +- darwin: 如果由 Frida 调用,则使 ThreadSuspendMonitor 直通,以避免死锁情况。感谢 [@mrmacete][]! +- darwin: 在 spawn() 期间,始终使用 PTY 进行管道 stdio,并启用 close-on-exec。 +- windows: 如果存在,使用现有的 DbgHelp 实例。感谢 [@fesily][]! +- windows: 实现 Module.enumerate_symbols()。感谢 [@fesily][]! +- process: 在 enumerate_modules() 中跳过隐藏的模块。 +- cloak: 添加 has_range_containing()。 +- elf-module: 用 get_interpreter() 替换 has_interp()。 +- gumjs: 修复运行时序列化无符号编码。 +- objc: 在方法上添加 symbol 属性,作为方法对象以人类可读的方式完全描述自身的一种手段。感谢 [@mrmacete][]! +- java: 使用符号作为唯一属性名称。感谢 [@yotamN][]! +- java: 向重载添加 toString() 方法。感谢 [@oriori1703][]! +- java: 向类型和字段添加 toString() 方法。感谢 [@yotamN][]! +- java: 修复重载方法的 toString()。感谢 [@yotamN][]! +- server: 支持作为共享库加载。感谢 [@Yannayli][]! + + +[@hsorbo]: https://twitter.com/hsorbo +[@mrmacete]: https://twitter.com/bezjaje +[@fesily]: https://github.com/fesily +[@yotamN]: https://github.com/yotamN +[@oriori1703]: https://github.com/oriori1703 +[@Yannayli]: https://twitter.com/Yannayli diff --git a/_i18n/cn/_posts/2023-04-15-frida-16-0-13-released.markdown b/_i18n/cn/_posts/2023-04-15-frida-16-0-13-released.markdown new file mode 100644 index 00000000..3a28072e --- /dev/null +++ b/_i18n/cn/_posts/2023-04-15-frida-16-0-13-released.markdown @@ -0,0 +1,14 @@ +--- +layout: news_item +title: 'Frida 16.0.13 发布' +date: 2023-04-15 01:34:01 +0200 +author: oleavr +version: 16.0.13 +categories: [release] +--- + +这次只有几个错误修复: + +- android: 修复旧系统上的动态链接器探测。 +- android: 将优雅注入限制为 arm 和 arm64。 +- linux: 修复 x86 和 x86_64 上的 syscall 等待逻辑。 diff --git a/_i18n/cn/_posts/2023-04-18-frida-16-0-14-released.markdown b/_i18n/cn/_posts/2023-04-18-frida-16-0-14-released.markdown new file mode 100644 index 00000000..07a2b25a --- /dev/null +++ b/_i18n/cn/_posts/2023-04-18-frida-16-0-14-released.markdown @@ -0,0 +1,20 @@ +--- +layout: news_item +title: 'Frida 16.0.14 发布' +date: 2023-04-18 00:49:44 +0200 +author: oleavr +version: 16.0.14 +categories: [release] +--- + +更多令人兴奋的错误修复: + +- linux: 修复 ProcMapsEntry 主/次设备号解析。感谢 [@chouex][]! +- interceptor: 修复 ARMv8 BTI 互操作性。感谢 [@zjw88282740][]! +- arm64-writer: 添加 put_ret_reg()。感谢 [@zjw88282740][]! +- compiler: 修复某些 Linux 系统(例如 Ubuntu 20.04)上的文件系统访问。感谢 [@pancake][] 报告并帮助追踪此问题! + + +[@chouex]: https://github.com/chouex +[@zjw88282740]: https://github.com/zjw88282740 +[@pancake]: https://twitter.com/trufae diff --git a/_i18n/cn/_posts/2023-04-19-frida-16-0-15-released.markdown b/_i18n/cn/_posts/2023-04-19-frida-16-0-15-released.markdown new file mode 100644 index 00000000..76cb6a0c --- /dev/null +++ b/_i18n/cn/_posts/2023-04-19-frida-16-0-15-released.markdown @@ -0,0 +1,16 @@ +--- +layout: news_item +title: 'Frida 16.0.15 发布' +date: 2023-04-19 00:20:30 +0200 +author: oleavr +version: 16.0.15 +categories: [release] +--- + +这是一个修复了影响 Apple 平台用户的两个稳定性问题的版本: + +- interceptor: 在 Darwin 上暂停和恢复之间保持线程端口处于活动状态。我们借用了 `enumerate_threads()` 释放的 Mach 线程端口权限名称,假设存在另一个引用来保持它们中的每一个处于活动状态。如果情况并非如此,我们充其量会将无效的 Mach 端口权限名称传递给 `thread_resume()`,最坏的情况是我们会使用完全不相关的端口。无论如何,我们会让这些线程永远处于暂停状态。感谢 [@mrmacete][]! +- agent: 在其自己的事务中处理 ThreadSuspendMonitor。由于 Interceptor 依赖于 ThreadSuspendMonitor 直通,因此在其自己的事务中以及在销毁时使用 Interceptor 的所有其他组件之后处理它,可以确保没有 Frida 线程会在拆卸时尝试在不可执行页面上执行代码。感谢 [@mrmacete][]! + + +[@mrmacete]: https://twitter.com/bezjaje diff --git a/_i18n/cn/_posts/2023-04-19-frida-16-0-16-released.markdown b/_i18n/cn/_posts/2023-04-19-frida-16-0-16-released.markdown new file mode 100644 index 00000000..1a823deb --- /dev/null +++ b/_i18n/cn/_posts/2023-04-19-frida-16-0-16-released.markdown @@ -0,0 +1,19 @@ +--- +layout: news_item +title: 'Frida 16.0.16 发布' +date: 2023-04-19 13:37:37 +0200 +author: oleavr +version: 16.0.16 +categories: [release] +--- + +鉴于 TypeScript 5.0 上个月发布,而 frida.Compiler 仍处于 4.9,我们认为通过升级它是时候了。所以在这个版本中,我们现在发布 5.0.4。此次升级还揭示了我们基于 V8 的运行时中的几个错误,以及我们嵌入的 frida-gum 类型定义稍微过时了。 + +享受吧! + +### 变更日志 + +- gumjs: 修复 V8 Isolate 拆卸期间的任务死锁。 +- gumjs: 修复 V8 快照创建期间的未定义行为。 +- compiler: 升级 frida-compile。 +- compiler: 使用正确的 @types/frida-gum。 diff --git a/_i18n/cn/_posts/2023-04-19-frida-16-0-17-released.markdown b/_i18n/cn/_posts/2023-04-19-frida-16-0-17-released.markdown new file mode 100644 index 00000000..c4dbba88 --- /dev/null +++ b/_i18n/cn/_posts/2023-04-19-frida-16-0-17-released.markdown @@ -0,0 +1,18 @@ +--- +layout: news_item +title: 'Frida 16.0.17 发布' +date: 2023-04-19 19:24:51 +0200 +author: oleavr +version: 16.0.17 +categories: [release] +--- + +是时候发布一个只有一个更改的错误修复版本了:事实证明,16.0.14 中 [introduced][] 的 ARMv8 BTI 互操作在 arm64e 模式下(即启用了指针身份验证)运行时,在 Apple A12+ SoC 上存在问题。 + +感谢 [@miticollo][] 报告并帮助分类原因,以及 [@mrmacete][] 进一步挖掘、集思广益潜在修复并实施 [fix][]。你们太棒了!❤️ + + +[introduced]: https://github.com/frida/frida-gum/commit/ea1e836e70cd5e7976bf680ff7771a5a4bc0a494 +[@miticollo]: https://github.com/miticollo +[@mrmacete]: https://twitter.com/bezjaje +[fix]: https://github.com/frida/frida-gum/commit/698b356fef0ecfc3ac2818f0b387be90e93deeda diff --git a/_i18n/cn/_posts/2023-04-22-frida-16-0-18-released.markdown b/_i18n/cn/_posts/2023-04-22-frida-16-0-18-released.markdown new file mode 100644 index 00000000..03568082 --- /dev/null +++ b/_i18n/cn/_posts/2023-04-22-frida-16-0-18-released.markdown @@ -0,0 +1,13 @@ +--- +layout: news_item +title: 'Frida 16.0.18 发布' +date: 2023-04-22 00:05:06 +0200 +author: oleavr +version: 16.0.18 +categories: [release] +--- + +只是一个快速的错误修复版本,恢复了对具有 ARM 仿真的 Android x86/x86_64 系统的支持。这仍然是我们 CI 中的盲点,我在开发新的 Linux 注入器时完全忘记了它。感谢 [@stopmosk][] 及时报告并帮助分类此回归。 + + +[@stopmosk]: https://github.com/stopmosk diff --git a/_i18n/cn/_posts/2023-04-27-frida-16-0-19-released.markdown b/_i18n/cn/_posts/2023-04-27-frida-16-0-19-released.markdown new file mode 100644 index 00000000..83a64c6d --- /dev/null +++ b/_i18n/cn/_posts/2023-04-27-frida-16-0-19-released.markdown @@ -0,0 +1,28 @@ +--- +layout: news_item +title: 'Frida 16.0.19 发布' +date: 2023-04-27 18:28:53 +0200 +author: oleavr +version: 16.0.19 +categories: [release] +--- + +这次有一些令人兴奋的稳定性改进: + +- darwin: 使 spawn() 意识到 iOS >= 15 上使用的 [prewarm][] 功能。发生的情况是,最常用的应用程序由 *dasd* 提前生成并保持挂起状态,直到用户尝试启动它们,以节省一些启动时间。这妨碍了 Frida 的 spawn 机制,因为: + - 以这种方式启动的应用程序不会在预期时通过 launchd,导致“超时已达到”错误,或更复杂的竞争条件 + - Frida 需要生成一个新的进程以确保早期插桩 + + 我们通过以下方式解决这些问题: + - 忽略来自 launchd 代理的 prewarm spawns(尽管如果启用了 spawn gating,它们仍会作为 spawn 发出) + - 在尝试生成它们之前杀死现有的 prewarmed 应用程序 + 感谢 [@mrmacete][]! +- glib: 确保 {Input,Output}Stream 仅在可轮询时 g_poll()。这在 Apple OS 和 BSD 上至关重要,其中 GLib 使用 kqueue 实现 GMainContext,否则当文件描述符表示普通文件时,它最终可能会永远等待。感谢 [@hsorbo][]! +- android: 通过改进 libffi 以避免有问题的重定位来修复 x86 devkits。 +- gadget: 修复 Windows 进程终止期间的死锁。感谢 [@Palacee-hun][] 的报告! + + +[prewarm]: https://developer.apple.com/documentation/uikit/app_and_environment/responding_to_the_launch_of_your_app/about_the_app_launch_sequence?language=objc +[@mrmacete]: https://twitter.com/bezjaje +[@hsorbo]: https://twitter.com/hsorbo +[@Palacee-hun]: https://github.com/Palacee-hun diff --git a/_i18n/cn/_posts/2023-06-23-frida-16-1-0-released.markdown b/_i18n/cn/_posts/2023-06-23-frida-16-1-0-released.markdown new file mode 100644 index 00000000..06f633fe --- /dev/null +++ b/_i18n/cn/_posts/2023-06-23-frida-16-1-0-released.markdown @@ -0,0 +1,550 @@ +--- +layout: news_item +title: 'Frida 16.1.0 发布' +date: 2023-06-23 23:31:22 +0200 +author: oleavr +version: 16.1.0 +categories: [release] +--- + +多年来,我一直梦想着将 Frida 带到用户空间软件之外,以支持检测 OS 内核以及裸机系统。甚至可能是微控制器... + +## 微控制器 + +今年早些时候,我家的猫门坏了。在与零售商反复沟通、仔细检查安装等之后,它会工作一小会儿,然后最终开始出现故障。 + +这对我们的猫来说显然没什么好玩的: + +![cat-door-fail](/img/cat-door-fail.jpg "disappointed cat") + +毫不奇怪,它们最终会制造很多噪音,进而使得不得不起来手动让它们进来时很难睡个好觉。 + +我最终买了第二个猫门,瞧,没有问题了。旧的那个最终积了一段时间的灰尘。我一直在想的是我是否可以调试它,甚至扩展软件以做更多有用的事情。 + +感到有冲动打开它去戳里面的电子设备,我最终屈服了: + +![cat-door-pcb](/img/cat-door-pcb.jpg "cat-door PCB"){: width="100%" } + +那看起来像是一个 STM32F030C6T6,这是一个基于 ARM Cortex M0 的 MCU。我的第一个想法是我是否可以转储闪存以进行一些静态分析。 + +在快速浏览 MCU 文档并进行一点万用表探测后,我弄清楚了 JP12 焊盘: + +| PAD 1/2 | | | PAD 7/8 | +| :-----: | :-------: | :---: | :-----: | +| BOOT0 | USART1 RX | SWDIO | GND | +| VDD | USART1 TX | | SWCLK | + +这使得拉高 *BOOT0* 变得容易,因此 MCU 启动到其内部引导加载程序而不是用户代码。 + +通过将 USB 转 3.3V TTL 设备连接到 USART1 焊盘,我可以转储闪存: + +{% highlight bash %} +$ ./stm32flash -r firmware.bin /dev/ttyUSB0 +stm32flash 0.7 + +http://stm32flash.sourceforge.net/ + +Interface serial_posix: 57600 8E1 +Version : 0x31 +Option 1 : 0x00 +Option 2 : 0x00 +Device ID : 0x0444 (STM32F03xx4/6) +- RAM : Up to 4KiB (2048b reserved by bootloader) +- Flash : Up to 32KiB (size first sector: 4x1024) +- Option bytes : 16b +- System memory : 3KiB +Memory read +Read address 0x08008000 (100.00%) Done. +{% endhighlight %} + +并执行一些静态分析: +![cat-door-firmware](/img/cat-door-firmware.png "cat-door firmware") + +鉴于另外两个焊盘连接到 SWDIO 和 SWCLK,用于串行线调试 (SWD),自然的下一步是将 [Raspberry Pi Debug Probe][] 连接到这些焊盘。设置好之后,我启动了 [OpenOCD][]: + +{% highlight bash %} +$ openocd -f interface/cmsis-dap.cfg -f target/stm32f0x.cfg +Open On-Chip Debugger 0.11.0-g8e3c38f7-dirty (2023-05-05-14:25) +Licensed under GNU GPL v2 +For bug reports, read + http://openocd.org/doc/doxygen/bugs.html +Info : auto-selecting first available session transport "swd". To override use 'transport select '. +Info : Listening on port 6666 for tcl connections +Info : Listening on port 4444 for telnet connections +Info : Using CMSIS-DAPv2 interface with VID:PID=0x2e8a:0x000c, serial=E6614103E78B482F +Info : CMSIS-DAP: SWD Supported +Info : CMSIS-DAP: FW Version = 2.0.0 +Info : CMSIS-DAP: Interface Initialised (SWD) +Info : SWCLK/TCK = 0 SWDIO/TMS = 0 TDI = 0 TDO = 0 nTRST = 0 nRESET = 0 +Info : CMSIS-DAP: Interface ready +Info : clock speed 1000 kHz +Info : SWD DPIDR 0x0bb11477 +Info : stm32f0x.cpu: hardware has 4 breakpoints, 2 watchpoints +Info : starting gdb server for stm32f0x.cpu on 3333 +Info : Listening on port 3333 for gdb connections +{% endhighlight %} + +我一直在思考的一个想法是添加一个新的 Frida 后端,您只能附加到 PID 0。在那里加载的任何脚本实际上都将在本地运行,并实现熟悉的 [JavaScript API][]。任何访问内存的 API,例如通过执行 *ptr('0x80000').readInt()* 取消引用 *int \** 时,最终都会查询目标,在上述情况下通过 SWD。 + +我最初开始草拟这个,后端将通过其 telnet 接口与 OpenOCD 守护进程对话。但我很快意识到与它的 GDB 兼容远程存根对话会更好。通过这种方式,Frida 将能够检测任何具有可用远程存根的目标。无论是 OpenOCD, [Corellium][] (iOS 内核检测!), QEMU 等。 + +至于 Interceptor,我的想法是基本功能将使用断点实现。但是,仅当用户提供 JavaScript 回调时。如果提供函数指针,我们可以执行内联 hook,以便目标可以在没有任何陷阱/与主机乒乓的情况下运行。这意味着它甚至可以用于观察和修改 OS 内核或 MCU 固件内的热代码。 + +经过一些初步草图,我能够运行以下脚本: + +{% highlight js %} +Interceptor.breakpointKind = 'hard'; + +const THUMB_BIT = 1; + +const initRest = ptr('0x0800306a').or(THUMB_BIT); +Interceptor.attach(initRest, { + onEnter(args) { + console.log('>>> init_rest()', + JSON.stringify(this.context, null, 2)); + }, + onLeave(retval) { + console.log(`<<< init_rest() retval=${retval}`); + } +}); +{% endhighlight %} + +使用 Frida REPL: + +{% highlight bash %} +$ frida -D barebone -p 0 -l demo.js + ____ + / _ | Frida 16.1.0 - A world-class dynamic instrumentation toolkit + | (_| | + > _ | Commands: + /_/ |_| help -> Displays the help system + . . . . object? -> Display information about 'object' + . . . . exit/quit -> Exit + . . . . + . . . . More info at https://frida.re/docs/home/ + . . . . + . . . . Connected to GDB Remote Stub (id=barebone) + +[Remote::SystemSession ]-> $gdb.continue() +[Remote::SystemSession ]-> >>> init_rest() { + "r7": "0xffffffff", + "pc": "0x800306a", + "r8": "0xffffffff", + "xPSR": "0x41000000", + "r9": "0xffffffff", + "sp": "0x20000578", + "r0": "0x0", + "r10": "0xffffffff", + "lr": "0x8003069", + "r1": "0x40021008", + "r11": "0xffffffff", + "r2": "0xffffffff", + "r12": "0xffffffff", + "r3": "0xffffffff", + "r4": "0xffffffff", + "r5": "0xffffffff", + "r6": "0xffffffff" +} +<<< init_rest() retval=0x1 +{% endhighlight %} + +这里有几点需要注意: + +- 我们将 *Interceptor.breakpointKind* 设置为 *hard*,因为我们的目标代码驻留在闪存中,这意味着软件断点将不起作用。如果我使用的是 [J-Link][] 或类似的 SWD 接口,这将是不必要的,因为添加软件断点时它会透明地重新刷新。 +- 后端尚未自动恢复,因此我们通过 *$gdb.continue()* 手动执行此操作,这是 [this internal API][] 的一部分,它向 JavaScript 公开了大部分 [GDB.Client][]。这旨在成为内部实现细节,但在新后端成熟时将需要它——它还不应被视为稳定的 API。 +- 我们设置最低有效位以向 Interceptor 指示目标函数使用 Thumb 指令编码。如果您以前在 32 位 ARM 上使用过 Frida 的常规后端,这部分可能已经很熟悉了。 +- 新的 Barebone 后端默认连接到 *127.0.0.1:3333* 处的 GDB 兼容远程存根。这与 OpenOCD 通常默认的情况相匹配,但可以通过设置 *FRIDA_BAREBONE_ADDRESS* 环境变量来覆盖。 + +## OS 内核 + +虽然我有趣的小猫门支线任务是该频谱微小部分的绝佳测试用例,但在支持更大的系统方面也有很大的潜力。 + +其中一个更酷的用例肯定是 Corellium,因为这意味着我们可以检测 iOS 内核。使用 [Tamarin Cable][],甚至应该可以在 checkm8 可利用的物理设备上使其工作。 + +不过在我们触及那个之前,让我们看看我们是否可以让 QEMU 和实时 Linux 内核一起运行。 + +### Linux + +首先,我们将启动一个我们可以玩的 VM: + +{% highlight bash %} +$ pip install arm_now +$ arm_now start aarch64 --add-qemu-options='-gdb tcp::9000' +... +Welcome to arm_now +buildroot login: +{% endhighlight %} + +接下来,我们将使用 Frida REPL 环顾四周: + +{% highlight bash %} +$ export FRIDA_BAREBONE_ADDRESS=127.0.0.1:9000 +$ frida -D barebone -p 0 + ____ + / _ | Frida 16.1.0 - A world-class dynamic instrumentation toolkit + | (_| | + > _ | Commands: + /_/ |_| help -> Displays the help system + . . . . object? -> Display information about 'object' + . . . . exit/quit -> Exit + . . . . + . . . . More info at https://frida.re/docs/home/ + . . . . + . . . . Connected to GDB Remote Stub (id=barebone) + +[Remote::SystemSession ]-> Process.arch +"arm64" +[Remote::SystemSession ]-> Process.enumerateRanges('r-x') +[ + { + "base": "0xffffff8008080000", + "protection": "r-x", + "size": 4259840 + } +] +[Remote::SystemSession ]-> $gdb.state +"stopped" +[Remote::SystemSession ]-> $gdb.exception +{ + "breakpoint": null, + "signum": 2, + "thread": {} +} +[Remote::SystemSession ]-> $gdb.exception.thread.readRegisters() +{ + "cpsr": 1610613189, + "pc": "0xffffff8008096648", + "sp": "0xffffff80085f3f10", + "x0": "0x0", + "x1": "0xffffff80085e6b78", + "x10": "0x880", + "x11": "0xffffffc00e877180", + "x12": "0x0", + "x13": "0xffffffc00ffe1f30", + "x14": "0x0", + "x15": "0xfffffff8", + "x16": "0xffffffbeff000000", + "x17": "0x0", + "x18": "0xffffffc00ffe17e0", + "x19": "0xffffff80085e0000", + "x2": "0x40079f5000", + "x20": "0xffffff80085f892c", + "x21": "0xffffff80085f88a0", + "x22": "0xffffff80085ffe80", + "x23": "0xffffff80085ffe80", + "x24": "0xffffff80085d5028", + "x25": "0x0", + "x26": "0x0", + "x27": "0x0", + "x28": "0x405a0018", + "x29": "0xffffff80085f3f10", + "x3": "0x30c", + "x30": "0xffffff800808492c", + "x4": "0x0", + "x5": "0x40079f5000", + "x6": "0x1", + "x7": "0x1c0", + "x8": "0x2", + "x9": "0xffffff80085f3e80" +} +[Remote::SystemSession ]-> +{% endhighlight %} + +您可能想知道我们是如何实现 *Process.enumerateRanges()* 的。这部分目前仅在 arm64 上实现,它是通过 [parsing the page tables][] 完成的。(如果我们正在与 Corellium 的远程存根对话,我们使用特定于供应商的监视器命令来节省大量网络往返。) + +所以现在我们正在窥视正在运行的内核,我们可能想做的一件事是查找内部函数和数据结构。这就是内存扫描 API 派上用场的地方: + +{% highlight js %} +for (const r of Process.enumerateRanges('r-x')) { + console.log(JSON.stringify(r, null, 2)); + const matches = Memory.scanSync(r.base, r.size, + '7b2000f0 fa03082a 992480d2 : 1f00009f ffffffff 1f00e0ff'); + console.log('Matches:', JSON.stringify(matches, null, 2)); +} +{% endhighlight %} + +在这里,我们正在寻找 Linux 内核的 [arm64 syscall handler][],匹配其前三条指令。我们使用掩码功能来掩盖 ADRP 和 MOV 指令(第一条和第三条指令)的立即数。 + +让我们试一试: + +{% highlight bash %} +$ frida -D barebone -p 0 -l scan.js + ____ + / _ | Frida 16.1.0 - A world-class dynamic instrumentation toolkit + | (_| | + > _ | Commands: + /_/ |_| help -> Displays the help system + . . . . object? -> Display information about 'object' + . . . . exit/quit -> Exit + . . . . + . . . . More info at https://frida.re/docs/home/ + . . . . + . . . . Connected to GDB Remote Stub (id=barebone) +Attaching... +{ + "base": "0xffffff8008080000", + "size": 4259840, + "protection": "r-x" +} +Matches: [ + { + "address": "0xffffff8008082f00", + "size": 12 + } +] +[Remote::SystemSession ]-> +{% endhighlight %} + +所以现在我们已经动态插桩到了内核的内部系统调用处理程序!🚀 + +重新实现内存扫描功能对我个人来说是亮点之一,因为 [@hsorbo][] 和我在结对编程中玩得很开心。该实现从概念上讲与我们在 jailed iOS 的 Fruity 后端和新的 Linux 注入器中所做的非常相似:我们可以只传输搜索算法在目标上运行,而不是将数据传输到主机并搜索它。 + +[memory scanner implementation][] 是用 Rust 编写的,并帮助为我将在本文稍后介绍的一个很酷的新功能奠定了基础。 + +所以,既然我们知道 Linux 内核的系统调用处理程序在哪里,我们可以使用 Interceptor 安装指令级 hook: + +{% highlight js %} +const el0Svc = ptr('0xffffff8008082f00'); +Interceptor.attach(el0Svc, function (args) { + const { context } = this; + const scno = context.x8.toUInt32(); + console.log(`syscall! scno=${scno}`); +}); +{% endhighlight %} + +并在我们正在运行的 VM 上尝试一下: + +{% highlight bash %} +$ frida -D barebone -p 0 -l kernhook.js + ____ + / _ | Frida 16.1.0 - A world-class dynamic instrumentation toolkit + | (_| | + > _ | Commands: + /_/ |_| help -> Displays the help system + . . . . object? -> Display information about 'object' + . . . . exit/quit -> Exit + . . . . + . . . . More info at https://frida.re/docs/home/ + . . . . + . . . . Connected to GDB Remote Stub (id=barebone) + +[Remote::SystemSession ]-> $gdb.continue() +[Remote::SystemSession ]-> syscall! scno=63 +syscall! scno=64 +syscall! scno=73 +syscall! scno=63 +syscall! scno=64 +syscall! scno=73 +syscall! scno=63 +syscall! scno=64 +syscall! scno=56 +syscall! scno=62 +syscall! scno=64 +syscall! scno=57 +syscall! scno=29 +syscall! scno=134 +... +{% endhighlight %} + +就是这样——我们正在监控整个系统的系统调用!💥 + +### Rust + +如果您尝试前面的示例,您可能会注意到的第一件事是我们大大减慢了系统的速度。这是因为当指定 JavaScript 函数作为回调时,Interceptor 使用断点。 + +不过不用担心。如果我们用机器码编写回调并传递 NativePointer,Interceptor 将选择不同的策略:它将修改目标的机器码以将执行重定向到蹦床,蹦床反过来调用我们指定地址的函数。 + +太好了。我们只需要将我们的机器码放入内存。你们中的一些人可能熟悉我们的 [CModule API][]。我们还没有在这个新的 Barebone 后端实现那个(我们最终会实现的!),但我们有更好的东西。输入 *RustModule*: + +{% highlight js %} +const kernBase = ptr('0xffffff8008080000'); +const procPidStatus = kernBase.add(0x15e600); + +const m = new RustModule(` +#[no_mangle] +pub unsafe extern "C" fn hook(ic: &mut gum::InvocationContext) -> () { + let regs = &mut ic.cpu_context; + println!("proc_pid_status() was called with x0={:#x} x1={:#x}", + regs.x[0], + regs.x[1], + ); +} +`); + +Interceptor.attach(procPidStatus, m.hook); +{% endhighlight %} + +RustModule 实现使用本地 Rust 工具链(假定在您的 PATH 上),将您提供的代码编译为 *no_std* 自包含 ELF。它重新定位此 ELF 并将其写入目标的内存。作为此过程的一部分,它还将解析 MMU 的页表并在那里插入新条目,以便上传的代码成为虚拟地址空间的一部分,其中页面是读/写/执行的。 + +在这个例子中,我们在我们的实时 Linux 内核中 hook [proc_pid_status()][]。 + +请注意,可以使用 *File.readAllText()* 来避免在 JavaScript 中内联 Rust 代码。为了简洁起见,我们在这里使用内联代码。 + +现在,有了我们 Rust 驱动的代理,让我们试一试: + +{% highlight bash %} +$ frida -D barebone -p 0 -l kernhook2.js + ____ + / _ | Frida 16.1.0 - A world-class dynamic instrumentation toolkit + | (_| | + > _ | Commands: + /_/ |_| help -> Displays the help system + . . . . object? -> Display information about 'object' + . . . . exit/quit -> Exit + . . . . + . . . . More info at https://frida.re/docs/home/ + . . . . + . . . . Connected to GDB Remote Stub (id=barebone) + +Error: to enable this feature, set FRIDA_BAREBONE_HEAP_BASE to the physical base address to use, e.g. 0x48000000 + at (/home/oleavr/src/demo/kernhook2.js:13) + at evaluate (native) + at (/frida/repl-2.js:1) + +[Remote::SystemSession ]-> +{% endhighlight %} + +哎呀!那不太行。我们的新后端仍然缺少一块:我们还没有任何“内核桥”到位,可以自动指纹识别已知内核的内部结构,以便找到我们可以使用的合适的内部内存分配器。这也将需要实现诸如 *Process.enumerateModules()* 之类的 API,这将允许列出加载的内核模块/kext。我们还可以定位内核的进程列表并实现 *enumerate_processes()*,以便 frida-ps 工作。这些只是几个例子... 将 frida-gadget 注入用户空间进程怎么样?对于我们想要避免修改闪存的嵌入式系统来说,这将非常有用。无论如何,我离题了 😊 + +因此,在 MCU 和未知内核上,如果您想使用 RustModule、内联 hook 模式下的 Interceptor、Memory.alloc() 等侵入性功能,您必须告诉 Frida 我们可能会破坏物理内存中的哪个位置。 + +考虑到这一点,让我们重试我们的示例,但这次我们将设置 *FRIDA_BAREBONE_HEAP_BASE* 环境变量: + +{% highlight bash %} +$ export FRIDA_BAREBONE_HEAP_BASE=0x48000000 +$ frida -D barebone -p 0 -l kernhook2.js + ____ + / _ | Frida 16.1.0 - A world-class dynamic instrumentation toolkit + | (_| | + > _ | Commands: + /_/ |_| help -> Displays the help system + . . . . object? -> Display information about 'object' + . . . . exit/quit -> Exit + . . . . + . . . . More info at https://frida.re/docs/home/ + . . . . + . . . . Connected to GDB Remote Stub (id=barebone) + +[Remote::SystemSession ]-> m +{ + "hook": "0xffffff80080103e0" +} +[Remote::SystemSession ]-> $gdb.continue() +{% endhighlight %} + +耶!🎉 所以现在,在我们运行 QEMU 的终端中,让我们尝试访问 */proc/$pid/status* 三次,以便调用 hook 函数: + +{% highlight bash %} +# head -3 /proc/self/status +Name: head +Umask: 0022 +State: R (running) +# head -3 /proc/self/status +Name: head +Umask: 0022 +State: R (running) +# head -3 /proc/self/status +Name: head +Umask: 0022 +State: R (running) +{% endhighlight %} + +在我们的 REPL 中,我们应该看到我们的 *hook()* 被击中三次: + +{% highlight bash %} +proc_pid_status() was called with x0=0xffffffc00d4bca00 x1=0xffffff8008608758 +proc_pid_status() was called with x0=0xffffffc00d4bc780 x1=0xffffff8008608758 +proc_pid_status() was called with x0=0xffffffc00d4bc780 x1=0xffffff8008608758 +{% endhighlight %} + +它有效!🥳 + +不过有一点很重要:在我们的示例中,我们使用了 *println!()*, 这实际上会导致目标击中断点,以便主机可以读出传递给它的消息,并像 JavaScript 中的 *console.log()* 一样将其冒泡。这意味着您应该只将此功能用于临时调试目的,如果在热代码路径上,请限制其调用频率。 + +您可能想做的下一件事是将外部符号传递到您的 RustModule 中。例如,如果您想从 Rust 代码调用内部内核函数。这是通过像这样声明它们来实现的: + +{% highlight rs %} +extern "C" { + fn frobnicate(data: *const u8, len: usize); +} +{% endhighlight %} + +然后在构造 RustModule 时,通过第二个参数将其传入: + +{% highlight js %} +const m = new RustModule(source, { + frobnicate: ptr('0xffffff8008084320'), +}); +{% endhighlight %} + +对于熟悉我们 CModule API 的人来说,这部分完全相同。您还可以使用 NativeCallback 在主机端(JavaScript 中)实现部分,但这需要小心处理以避免性能瓶颈。反方向也有 NativeFunction,您可以使用它从 JavaScript 调用 Rust 代码。 + +最后但并非最不重要的一点是,您可能还想从 [crates.io][] 导入现有的 Rust crate。这也受支持: + +{% highlight js %} +const m = new RustModule(source, {}, { + dependencies: [ + 'cstr_core = { version = "0.2.6", default-features = false }', + ] +}); +{% endhighlight %} + +## Corellium + +令人兴奋的是,上述所有 Linux 位在 Corellium 上也“正常工作”。您所要做的就是将 *FRIDA_BAREBONE_ADDRESS* 指向 Corellium UI 中“Advanced Options” -> “gdb”下显示的端点。 + +感谢 Corellium 的优秀人员在做这件事时的支持。他们甚至实现了新的协议功能以提高互操作性 🔥 + +## 未来 + +这个新后端目前应被视为 alpha 质量,但我认为它已经能够做很多有用的事情,把它放在分支上太可惜了。 + +您可能会注意到实现的 JS API 仅涵盖子集,并且并非所有功能都在非 arm64 目标上可用。但随着后端的成熟,所有这些都会得到改善。(非常欢迎 Pull-request!) + +作为一个有趣的旁注,这是附加到 BeOS 内核的 Frida: + +![beos-kernel](/img/barebone-beos.png "BeOS kernel"){: width="100%" } + +## EOF + +还有很多其他令人兴奋的更改,所以一定要查看下面的变更日志。 + +享受吧! + +### 变更日志 + +- 添加 Barebone 后端。(上面已广泛介绍。) +- objc: 处理修饰符。这使得类型解析更可靠,尤其是在处理通常具有“atomic”修饰符的 ivar 时:由于修饰符最终被视为未知类型而引发异常。感谢 [@mrmacete][]! +- android: 修复对 Android 14 的支持。感谢 [@gsingh93][]!也感谢 [@jayluxferro][] 为我在合并 [@gsingh93][] 的 PR 时犯的错误贡献了后续修复。 +- gum-graft: 添加对链式导入的支持。感谢 [@mrmacete][]! +- gumjs: 添加 NativePointer#readVolatile(),提供一种安全的方式来读取可能在中途取消映射或更改其内存保护的内存。感谢 [@hsorbo][]! +- darwin: 改进 tvOS 支持以涵盖 frida-server。感谢 [@tmm1][]! +- darwin: 堵塞回退 kill() 逻辑中的内存泄漏。感谢 [@tmm1][]! +- fruity: 处理获取 dyld 符号失败。 +- compiler: 将 frida-compile 升级到 16.2.2。依赖项的源映射现在也已捆绑——感谢 [@vfsfitvnm][]! +- compiler: 将 @types/frida-gum 升级到 18.3.2,现在具有改进的 *hexdump()* 类型定义。 +- gdb: 添加 GDB.Client,通过剔除 Fruity 的 LLDB.Client 的核心,并在其之上添加许多协议增强和互操作性修复。 +- elf-module: 改进 API 并使其跨平台。支持从 blob 加载,公开重定位,并提高整体稳健性。 +- capstone: 修复使用 MSVC 构建时 x86 上的崩溃。 + + +[Raspberry Pi Debug Probe]: https://www.raspberrypi.com/products/debug-probe/ +[OpenOCD]: https://openocd.org/ +[JavaScript API]: /docs/javascript-api/ +[Corellium]: https://www.corellium.com/ +[J-Link]: https://www.segger.com/products/debug-probes/j-link/technology/flash-breakpoints/ +[this internal API]: https://github.com/frida/frida-core/blob/0c6737becb603871f62c775f06214b27c3e208ad/src/barebone/script.vala#L220-L257 +[GDB.Client]: https://github.com/frida/frida-core/blob/0c6737becb603871f62c775f06214b27c3e208ad/src/gdb.vala#L3 +[Tamarin Cable]: https://github.com/stacksmashing/tamarin-firmware +[parsing the page tables]: https://github.com/frida/frida-core/blob/0c6737becb603871f62c775f06214b27c3e208ad/src/barebone/arch-arm64/machine.vala#L38-L91 +[arm64 syscall handler]: https://github.com/torvalds/linux/blob/569dbb88e80deb68974ef6fdd6a13edb9d686261/arch/arm64/kernel/entry.S#L800-L802 +[@hsorbo]: https://twitter.com/hsorbo +[memory scanner implementation]: https://github.com/frida/frida-core/tree/0c6737becb603871f62c775f06214b27c3e208ad/src/barebone/helpers +[CModule API]: /docs/javascript-api/#cmodule +[proc_pid_status()]: https://github.com/torvalds/linux/blob/569dbb88e80deb68974ef6fdd6a13edb9d686261/fs/proc/array.c#L372-L391 +[crates.io]: https://crates.io/ +[@mrmacete]: https://twitter.com/bezjaje +[@gsingh93]: https://github.com/gsingh93 +[@jayluxferro]: https://github.com/jayluxferro +[@tmm1]: https://twitter.com/tmm1 +[@vfsfitvnm]: https://github.com/vfsfitvnm diff --git a/_i18n/cn/_posts/2023-07-01-frida-16-1-1-released.markdown b/_i18n/cn/_posts/2023-07-01-frida-16-1-1-released.markdown new file mode 100644 index 00000000..5e9ba660 --- /dev/null +++ b/_i18n/cn/_posts/2023-07-01-frida-16-1-1-released.markdown @@ -0,0 +1,18 @@ +--- +layout: news_item +title: 'Frida 16.1.1 发布' +date: 2023-07-01 06:42:06 +0200 +author: oleavr +version: 16.1.1 +categories: [release] +--- + +这次只有几个变化: + +- compiler: 将 frida-compile 升级到 16.3.0,现在包含 TypeScript 5.1.5 和其他改进。其中包括 [@hsorbo][] 的修复,该修复将默认 *moduleResolution* 更改为 *Node16*。 +- stalker: 添加 Iterator.get_capstone(),以便 transformer 可以使用需要 Capstone 句柄的 Capstone API。 +- node: 修复 RPC 消息数组检查。感谢 [@ZachQin][]! + + +[@hsorbo]: https://twitter.com/hsorbo +[@ZachQin]: https://github.com/ZachQin diff --git a/_i18n/cn/_posts/2023-07-11-frida-16-1-2-released.markdown b/_i18n/cn/_posts/2023-07-11-frida-16-1-2-released.markdown new file mode 100644 index 00000000..f19b8d71 --- /dev/null +++ b/_i18n/cn/_posts/2023-07-11-frida-16-1-2-released.markdown @@ -0,0 +1,17 @@ +--- +layout: news_item +title: 'Frida 16.1.2 发布' +date: 2023-07-11 17:44:02 +0200 +author: oleavr +version: 16.1.2 +categories: [release] +--- + +是时候发布新版本来改进一些事情了: + +- darwin: 修复 Stalker.follow() 回归,其中正在进行的系统调用会被残酷地中断,通常导致目标崩溃。感谢结对编程,[@hsorbo][]! +- gumjs: 为 QuickJS 实现 WeakRef API。 +- compiler: 将 @types/frida-gum 升级到 18.4.0。 + + +[@hsorbo]: https://twitter.com/hsorbo diff --git a/_i18n/cn/_posts/2023-07-14-frida-16-1-3-released.markdown b/_i18n/cn/_posts/2023-07-14-frida-16-1-3-released.markdown new file mode 100644 index 00000000..591d8ab7 --- /dev/null +++ b/_i18n/cn/_posts/2023-07-14-frida-16-1-3-released.markdown @@ -0,0 +1,21 @@ +--- +layout: news_item +title: 'Frida 16.1.3 发布' +date: 2023-07-14 13:18:10 +0200 +author: oleavr +version: 16.1.3 +categories: [release] +--- + +是时候发布新版本了,正好赶上周末: + +- server: 添加 iOS >= 17 缺失的 entitlement。感谢 [@alexhude][] 帮助弄清这个问题。 +- stalker: 改进 arm 和 arm64 上的独占存储处理。我们不再可能扩展当前块以包含超出块自然结束位置的指令,而是采用更安全的方法:一旦遇到独占存储,我们就回顾以前生成的块,看看是否能找到一个带有独占加载的块。如果找到,我们将此块范围标记为使用独占访问。我们还会使块无效,以便在重新编译时省略有问题的检测。为了也允许自定义 transformer 调整其生成的代码,我们引入了 StalkerIterator.get_memory_access()。感谢有趣且富有成效的结对编程,[@hsorbo][]! +- gumjs: 添加 *StalkerIterator.memoryAccess*,允许自定义 transformer 确定添加什么样的检测是安全的,而不会干扰独占存储操作。设置为 'open'(当诸如 callout 之类的“嘈杂”检测是安全的时)或 'exclusive'(当此类检测有风险并可能导致无限循环时)。(由于独占存储失败,随后的每次重试也会失败。) +- gumjs: 修复 arm 上 Stalker 的 CModule 绑定。 +- stalker: 修复添加的 slab 中失效时的崩溃。 +- arm64-writer: 添加 put_eor_reg_reg_reg()。感谢有趣且富有成效的结对编程,[@hsorbo][]! + + +[@alexhude]: https://github.com/alexhude +[@hsorbo]: https://twitter.com/hsorbo diff --git a/_i18n/cn/_posts/2023-08-29-frida-16-1-4-released.markdown b/_i18n/cn/_posts/2023-08-29-frida-16-1-4-released.markdown new file mode 100644 index 00000000..6c56de59 --- /dev/null +++ b/_i18n/cn/_posts/2023-08-29-frida-16-1-4-released.markdown @@ -0,0 +1,21 @@ +--- +layout: news_item +title: 'Frida 16.1.4 发布' +date: 2023-08-29 15:58:08 +0200 +author: oleavr +version: 16.1.4 +categories: [release] +--- + +这次有一些令人兴奋的改进: + +- ios: 修复 iOS 17 上的 spawn()。感谢 [@hsorbo][]! +- ios: 添加对 rootless 系统的支持。感谢结对编程,[@hsorbo][]! +- android: 修复动态链接器兼容性回归。感谢结对编程,[@hsorbo][]!感谢 [@getorix][] 的报告。 +- gumjs: 添加 Worker API,以便可以将繁重的处理移至后台线程,从而允许及时处理 hook。目前仅在 QuickJS 运行时中实现。感谢 [@mrmacete][] 追踪并修复实现中的最后一分钟错误。 +- linux: 改进尝试附加到濒临死亡的进程时的错误处理。 + + +[@hsorbo]: https://x.com/hsorbo +[@getorix]: https://x.com/getorix +[@mrmacete]: https://x.com/bezjaje diff --git a/_i18n/cn/_posts/2023-11-04-frida-16-1-5-released.markdown b/_i18n/cn/_posts/2023-11-04-frida-16-1-5-released.markdown new file mode 100644 index 00000000..915c56a4 --- /dev/null +++ b/_i18n/cn/_posts/2023-11-04-frida-16-1-5-released.markdown @@ -0,0 +1,82 @@ +--- +layout: news_item +title: 'Frida 16.1.5 发布' +date: 2023-11-04 21:11:20 +0100 +author: oleavr +version: 16.1.5 +categories: [release] +--- + +自上次发布以来,[@hsorbo][] 和我在各种令人兴奋的技术上进行了很多有趣的结对编程。让我们直接潜入。 + +## Swift + +我们为 Swift 引入了一个全新的 ApiResolver,您可以像这样使用它: + +{% highlight js %} +const r = new ApiResolver('swift'); +r.enumerateMatches('functions:*CoreDevice!*RemoteDevice*') +.forEach(({ name, address }) => { + console.log('Found:', name, 'at:', address); +}); +{% endhighlight %} + +还有一个令人兴奋的新 frida-tools 版本 12.3.0,它使用新的 ApiResolver 升级了 frida-trace 以支持 Swift 跟踪: + +{% highlight bash %} +$ frida-trace Xcode -y '*CoreDevice!*RemoteDevice*' +{% endhighlight %} + +## Module + +我们的 Module API 现在还提供 *enumerateSections()* 和 *enumerateDependencies()*。当您想扫描加载的模块以查找特定的部分名称时,我们现有的 *module* ApiResolver 现在可以让您轻松做到这一点: + +{% highlight js %} +const r = new ApiResolver('module'); +r.enumerateMatches('sections:*!*text*/i') +.forEach(({ name, address }) => { + console.log('Found:', name, 'at:', address); +}); +{% endhighlight %} + +## EOF + +还有很多其他令人兴奋的更改,所以一定要查看下面的变更日志。 + +享受吧! + +### 变更日志 + +- swift-api-resolver: 添加全新的 Swift API Resolver。 +- module-api-resolver: 支持解析部分。 +- api-resolver: 向匹配项添加可选的 *size* 字段。 +- module: 添加 enumerate_sections()。 +- module: 添加 enumerate_dependencies()。 +- device: 添加 unpair()。目前仅针对 iOS 设备实现。 +- compiler: 将 frida-compile 升级到 16.4.1,将 @types/frida-gum 升级到 18.4.5。 +- gdb: 处理空响应数据包。 +- gdb: 处理对功能文档请求的错误回复。 +- darwin-mapper: 加载时初始化 TLV 描述符。感谢 [@fabianfreyer][]! +- darwin-module: 添加线程局部变量 API。感谢 [@fabianfreyer][]! +- darwin-module: 稍微优化导出枚举。 +- elf-module: 改进部分 ID 生成。 +- x86-writer: 添加基于 reg-reg {fs,gs} 的 MOV 指令。感谢 [@fabianfreyer][]! +- arm64-writer: 添加 MRS 指令。感谢 [@fabianfreyer][]! +- arm64-writer: 添加 UBFM, LSL 和 LSR 指令。感谢 [@fabianfreyer][]! +- relocator: 改进 arm64 上的暂存寄存器策略。 +- interceptor: 使用计算出的暂存寄存器分支到蹦床。 +- interceptor: 在 arm64 上重新定位微小目标。 +- linux: 处理禁用的 process_vm_{read,write}v()。感谢 [@Pyraun][]! +- server: 在 rootless iOS 上使用 sysroot 作为临时文件。感谢 [@fabianfreyer][]! +- gumjs: 修复 Interceptor 不存在时 File 和 Database 中的崩溃。感谢 [@mrmacete][]! +- gumjs: 修复 32 位 BE 的 NativePointer from number (#752)。感谢 [@forky2][]! +- gumjs: 将 frida-swift-bridge 升级到 2.0.7。 +- ci: 发布 Node.js 20 & 21 以及 Electron 27 的预构建版本。 +- ci: 暂时不发布 Swift 绑定。有一个长期存在的 heisenbug 导致 x86_64 切片随机损坏,进而导致 CI 发布作业失败。鉴于使用下载的核心 devkit 在本地构建这些绑定是多么容易,我不太想很快投入时间来解决这个问题,简单地删除发布资产似乎是最好的解决方案。 + + +[@hsorbo]: https://x.com/hsorbo +[@fabianfreyer]: https://github.com/fabianfreyer +[@Pyraun]: https://github.com/Pyraun +[@mrmacete]: https://x.com/bezjaje +[@forky2]: https://github.com/forky2 diff --git a/_i18n/cn/_posts/2023-11-13-frida-16-1-6-released.markdown b/_i18n/cn/_posts/2023-11-13-frida-16-1-6-released.markdown new file mode 100644 index 00000000..014591b7 --- /dev/null +++ b/_i18n/cn/_posts/2023-11-13-frida-16-1-6-released.markdown @@ -0,0 +1,16 @@ +--- +layout: news_item +title: 'Frida 16.1.6 发布' +date: 2023-11-13 22:43:57 +0100 +author: oleavr +version: 16.1.6 +categories: [release] +--- + +只是一个快速的错误修复版本,用于回滚上一版本中进入的两个 Interceptor/Relocator arm64 更改。事实证明,这些需要在落地之前进行更多改进,因此我们暂时将其回滚。 + +### 变更日志 + +- Revert "relocator: Improve scratch register strategy on arm64". +- Revert "interceptor: Relocate tiny targets on arm64". +- stalker: 允许 transformer 在 arm64 上跳过调用。 diff --git a/_i18n/cn/_posts/2023-11-16-frida-16-1-7-released.markdown b/_i18n/cn/_posts/2023-11-16-frida-16-1-7-released.markdown new file mode 100644 index 00000000..6f10e3f4 --- /dev/null +++ b/_i18n/cn/_posts/2023-11-16-frida-16-1-7-released.markdown @@ -0,0 +1,21 @@ +--- +layout: news_item +title: 'Frida 16.1.7 发布' +date: 2023-11-16 09:01:31 +0100 +author: oleavr +version: 16.1.7 +categories: [release] +--- + +这次有一些整洁的改进: + +- stalker: 允许 transformer 在 x86 上跳过调用。感谢 [@s1341][]! +- objc: 容忍损坏的 Swift 类名。感谢 [@hsorbo][]! +- cpu-features: 改进 AVX2 检测。感谢协助,[@smx-smx][]! +- windows: 添加对 MinGW 的支持。感谢协助,[@smx-smx][]! +- openssl: 升级到 openssl-3.0.12+quic@b81f0ae。 + + +[@s1341]: https://github.com/s1341 +[@hsorbo]: https://x.com/hsorbo +[@smx-smx]: https://github.com/smx-smx diff --git a/_i18n/cn/_posts/2023-11-28-frida-16-1-8-released.markdown b/_i18n/cn/_posts/2023-11-28-frida-16-1-8-released.markdown new file mode 100644 index 00000000..1f2aa647 --- /dev/null +++ b/_i18n/cn/_posts/2023-11-28-frida-16-1-8-released.markdown @@ -0,0 +1,17 @@ +--- +layout: news_item +title: 'Frida 16.1.8 发布' +date: 2023-11-28 22:06:07 +0100 +author: oleavr +version: 16.1.8 +categories: [release] +--- + +这次有三个令人兴奋的变化: + +- process: 添加 *get_main_module()*,作为 *Process.mainModule* 暴露给 JavaScript。当需要知道哪个模块代表进程的主可执行文件时很有用。过去,这通常是通过枚举加载的模块并假设列表中的第一个就是它来实现的。在最新的 Apple 操作系统上情况不再如此,因此我们通过这个新 API 提供了一个高效且可移植的解决方案。感谢 [@mrmacete][]! +- compiler: 将 @types/frida-gum 升级到 18.5.0,现在包含最近 API 添加的类型定义。 +- barebone: 修复与最新 Corellium 的兼容性。 + + +[@mrmacete]: https://x.com/bezjaje diff --git a/_i18n/cn/_posts/2023-12-20-frida-16-1-9-released.markdown b/_i18n/cn/_posts/2023-12-20-frida-16-1-9-released.markdown new file mode 100644 index 00000000..24d70d64 --- /dev/null +++ b/_i18n/cn/_posts/2023-12-20-frida-16-1-9-released.markdown @@ -0,0 +1,22 @@ +--- +layout: news_item +title: 'Frida 16.1.9 发布' +date: 2023-12-20 00:40:51 +0100 +author: oleavr +version: 16.1.9 +categories: [release] +--- + +此版本中有相当多的好东西: + +- interceptor: 也暂停隐藏的线程。这可以防止在使用 Interceptor hook 与内部可能使用的任何函数位于同一页面上的函数时,我们自己的线程发生随机 SIGBUS 崩溃。感谢 [@mrmacete][]! +- darwin: 迁移到我们的 POSIX Exceptor 后端。Mach 异常处理 API 在最近的 Apple OS 版本中变得越来越受限。 +- darwin: 解析 arm64 上的导入蹦床,允许我们 hook 诸如 sigaction() 之类的目标。 +- linux: 改进 spawn() 以处理再次命中 r_brk 的情况。 +- linker: 改进 spawn() 以考虑 RTLD 符号的磁盘 ELF。这意味着我们可能会在其他 Android 系统上找到 r_debug。 +- linux: 修复 DT_INIT_ARRAY 包含哨兵值时的 spawn()。 +- linux: 改进 spawn() 以使用 DT_PREINIT_ARRAY(如果存在)。 +- android: 处理 RTLD 回退逻辑中的符号链接。 + + +[@mrmacete]: https://x.com/bezjaje diff --git a/_i18n/cn/_posts/2023-12-24-frida-16-1-10-released.markdown b/_i18n/cn/_posts/2023-12-24-frida-16-1-10-released.markdown new file mode 100644 index 00000000..9569b94b --- /dev/null +++ b/_i18n/cn/_posts/2023-12-24-frida-16-1-10-released.markdown @@ -0,0 +1,18 @@ +--- +layout: news_item +title: 'Frida 16.1.10 发布' +date: 2023-12-24 09:04:29 +0100 +author: oleavr +version: 16.1.10 +categories: [release] +--- + +一些整洁的小错误修复,正好赶上圣诞节: + +- server: 添加 iOS 16 缺失的 entitlement,这是在内存中重新映射二进制文件所必需的。感谢 [@as0ler][]! +- android: 修复 Android 14 上解释器运行方法的 Java hook。 +- 修复 frida-server 和 frida-inject 等 CLI 工具中显示的 argv[0]。感谢 [@bet4it][]! + + +[@as0ler]: https://x.com/as0ler +[@bet4it]: https://github.com/bet4it diff --git a/_i18n/cn/_posts/2024-01-12-frida-16-1-11-released.markdown b/_i18n/cn/_posts/2024-01-12-frida-16-1-11-released.markdown new file mode 100644 index 00000000..fe5367d8 --- /dev/null +++ b/_i18n/cn/_posts/2024-01-12-frida-16-1-11-released.markdown @@ -0,0 +1,30 @@ +--- +layout: news_item +title: 'Frida 16.1.11 发布' +date: 2024-01-12 15:13:16 +0100 +author: oleavr +version: 16.1.11 +categories: [release] +--- + +这次有很多好东西: + +- stalker: 在多个方面提高稳定性。感谢 [@as0ler][], [@hsorbo][] 和 [@mrmacete][] 进行的有趣且富有成效的 mob 编程会议,从而产生了这些精彩的改进: + - stalker: 在 arm64 上复制排除调用的 BLR,而不是用功能等效的调用替换它们,以便按预期使用任何指针身份验证上下文。感谢 [@mrmacete][]! + - stalker: 当 allocate_near() 在 arm64 上失败时中止,而不是由于随后的 NULL 指针取消引用而崩溃。 + - gumjs: 修复 Stalker.flush() 在停止的 sink 上的崩溃。如果刚刚调用了 Stalker.garbageCollect(),就会发生这种情况。 + - gumjs: 修复 Stalker QuickJS 回调逻辑中的 use-after-free。我们需要保持回调值处于活动状态,以防 Stalker.garbageCollect() 在中间发生并释放它们。 +- darwin: 改进符号化器缓存失效逻辑。感谢 [@mrmacete][]! +- swift-api-resolver: 处理签名指针。 +- linux: 改进 spawn() 以处理部分链接映射。 +- linux: 改进注入器以处理 XOM 页面。 +- linux: 改进注入器 RTLD API 检测。 +- linux: 修复注入器 ELF SYMTAB 名称解析。 +- node: 在 UNIX 上链接 inspector 库,以修复调用 Script#enableDebugger() 时的 RTLD panic。感谢 [@pandasauce][]! +- ci: 发布 Node.js 20 和 Electron 27 的 FreeBSD 预构建版本。 + + +[@as0ler]: https://twitter.com/as0ler +[@hsorbo]: https://twitter.com/hsorbo +[@mrmacete]: https://twitter.com/bezjaje +[@pandasauce]: https://github.com/pandasauce diff --git a/_i18n/cn/_posts/2024-02-16-frida-16-2-0-released.markdown b/_i18n/cn/_posts/2024-02-16-frida-16-2-0-released.markdown new file mode 100644 index 00000000..5f8284ad --- /dev/null +++ b/_i18n/cn/_posts/2024-02-16-frida-16-2-0-released.markdown @@ -0,0 +1,127 @@ +--- +layout: news_item +title: 'Frida 16.2.0 发布' +date: 2024-02-16 13:45:43 +0100 +author: oleavr +version: 16.2.0 +categories: [release] +--- + +这次有很多令人兴奋的新事物。让我们直接潜入。 + +## 线程名称 + +Process.enumerateThreads() API 现在还在可用时公开线程名称: + +{% highlight sh %} +$ frida -U -F +[Pixel 6 Pro::com.google.android.calculator ]-> Process.enumerateThreads()[1] +{ + "id": 9579, + "name": "Signal Catcher", + "state": "waiting", + "context": { … } +} +[Pixel 6 Pro::com.google.android.calculator ]-> +{% endhighlight %} + +感谢 [@Hexploitable][] 提供了让这个球滚动起来的很棒的 pull-request 🙌 + +## 内存保护查询 + +有时必须快速确定内存中页面的当前保护。感谢 [@mrmacete][],现在您可以: + +{% highlight sh %} +$ frida -p 0 +[Local::SystemSession ]-> Memory.queryProtection(Module.getExportByName(null, 'open')) +"r-x" +[Local::SystemSession ]-> +{% endhighlight %} + +## QuickJS 2024-01-13 + +此版本还包含上个月发布的最新 QuickJS。这意味着几乎完全支持 ES2023,甚至还有即将推出的 ES2024 规范中的一些功能。另外,还有相当多的错误修复。值得一提的是,现在支持顶级 await,这使得编写在我们的两个 JavaScript 运行时上都能工作的可移植脚本变得更加容易。 + +## 隐身 (Cloaking) + +Frida 跟踪自己的内存范围、线程等,以防止您在进程内省期间看到自己。诸如 Process.enumerateThreads() 之类的内省 API 确保 Frida 自己的资源被隐藏,并且事情看起来就像您不在被插桩的进程中一样。 + +对于那些使用 Stalker 或其他将您暴露给原始内存位置的人来说,您可能会看到不属于任何加载模块的代码,并想知道它来自哪里。例如,如果您使用 Stalker.follow() 跟踪执行进入或离开 hook 函数,它将执行 Interceptor 生成的一些 Trampoline (蹦床) 代码。 + +使用 Gum C API 的代理已经可以查询给定的内存地址、线程等是否归 Frida 所有,但这尚未暴露给我们的 JavaScript 绑定。但是现在,感谢 [@mrmacete][] 的另一个很棒的贡献,现在支持这一点。例如: + +{% highlight sh %} +$ frida -p 0 +[Local::SystemSession ]-> open = Module.getExportByName(null, 'open') +"0x7f929a325840" +[Local::SystemSession ]-> Interceptor.attach(open, () => {}) +{} +[Local::SystemSession ]-> Instruction.parse(open).toString() +"jmp 0x7f928940c408" +[Local::SystemSession ]-> Cloak.hasRangeContaining(ptr('0x7f928940c408')) +true +[Local::SystemSession ]-> pointInsideOpen = open.add(16) +"0x7f929a325850" +[Local::SystemSession ]-> Instruction.parse(pointInsideOpen).toString() +"push rbx" +[Local::SystemSession ]-> Cloak.hasRangeContaining(pointInsideOpen) +false +[Local::SystemSession ]-> +{% endhighlight %} + +## Fast Interceptor (快速拦截器) + +一个鲜为人知且相当新的功能,现在也可以从 JavaScript 获得,即 Interceptor.replaceFast()。它的不同之处在于目标被修改为直接向量到您的替换,这意味着与 Interceptor.replace() 相比开销更小。这也意味着如果您想调用原始实现,则需要使用返回的指针。也不可能将其与同一目标的 Interceptor.attach() 结合使用。但是,如果您正在处理热门目标,这绝对是您希望在工具箱中拥有的东西,尤其是与 CModule 结合使用时。 + +## ELF 导出回归 + +我们很晚才发现 Frida 16.1.0 破坏了一些 ELF 二进制文件上的 Module#enumerateExports(),这现在终于修复了。事实证明这是我在使 Gum.ElfModule 成为跨平台 API 时添加的边界检查中的一个错误,当时它获得了对离线用例的支持,例如我们的 Barebone 后端的用例。(我们在那里使用它动态地将 Rust 代码注入 OS 内核和裸机目标。) + +## ELF 导入槽 + +那些在 Apple 平台上使用 Frida 的人可能已经注意到 Module#enumerateImports() 为您提供的导入对象总是有一个 *slot* 属性。您可以使用它的一种方法是将新指针写入该地址,让您按模块重新绑定导入。如果 Interceptor 无法 hook 函数,或者对于您想要避免内联 hook 的场景,这非常方便。 + +从这个版本开始,我们现在也在基于 ELF 的平台上提供 *slot* 属性,因此您也可以在 Linux, Android, FreeBSD 和 QNX 上使用此功能。耶! + +## Android 稳定性 + +最近版本的 Android 上的狂热用户可能经历过“软循环”,即 Frida 随机使 Zygote 崩溃并导致一大块用户空间重启。事实证明这是由于运行时在等待线程数降至一时(即等待进程变为单线程)通过轮询 /proc/self/stat 来准备 fork() 造成的。 + +现在通过减去 Frida 拥有的线程来解决这个问题,我们通过内部使用 Cloak API 来确定这一点,本文前面已经提到过。我们还利用了新的 ELF 导入槽功能,因此我们可以仅为 libart.so 中的调用者 hook read()。在这种情况下,在 libc.so 的 read() 中插入内联 hook 不是一个好主意,因为我们所在的进程可能会将其用于无限期阻塞的阻塞读取。当想要卸载并卡在等待回滚我们的内联 hook 的机会时,这成为一个挑战。 + +无论如何,这是一个非常有趣的错误。感谢 [@enovella_][] 报告并帮助追踪此问题 🙌 + +在谈论 Android 时,此版本还改进了 Android 14 上的 Java hook,其中在 *--enable-optimizations* 的情况下使用了新的 ART 快速入口点。感谢 [@cr4zyserb][] 的这一巧妙贡献。 + +## 更好的 iOS 16 支持 + +如果您在尝试在 iOS 上安装 Frida 时遇到 `Service cannot load in requested session`,这现在终于修复了。感谢 [@as0ler][] 协助修复此问题。 + +## 越狱 tvOS + +另一个令人兴奋的发展是我们现在支持越狱的 tvOS,您可以直接从我们的 repo https://build.frida.re/ 获取 .deb。特别感谢 [@tmm1][] 提供了实现此目的的 pull-request。 + +## 杂项 + +此版本还有更多内容。其余更改如下: + +- symbolutil-libdwarf: 修复 DWARF 5 中 *DW_AT_ranges* 的处理。这意味着 DebugSymbol API 在由较新工具链构建的二进制文件上可以正常工作。 +- elf-module: 修复在线时的重定位地址。 +- interceptor: 在 arm64 上声明 graft 时检查模块前缀,以兼容 Xcode 的新 libLogRedirect.dylib 在 iOS 17 上的插入。感谢 [@mrmacete][]! +- interceptor: 在 arm64 上隐藏 thunk。感谢 [@mrmacete][]! +- windows: 确保 MSVC 源字符集设置为 UTF-8,以便可以在运行时正确解析嵌入的 JavaScript。感谢 [@Qfrost911][]! +- freebsd: 改进 allocate-near 策略。 +- gumjs: 堵塞 QJS load() w/ ESM 期间的泄漏。 +- objc: 确保块结构在实现设置器中是可写的。感谢 [@mrmacete][]! +- compiler: 升级 @types/frida-gum 到 18.6.0。 + +享受吧! + + +[@Hexploitable]: https://twitter.com/Hexploitable +[@mrmacete]: https://twitter.com/bezjaje +[@enovella_]: https://twitter.com/enovella_ +[@cr4zyserb]: https://github.com/cr4zyserb +[@as0ler]: https://twitter.com/as0ler +[@tmm1]: https://twitter.com/tmm1 +[@Qfrost911]: https://github.com/Qfrost911 diff --git a/_i18n/cn/_posts/2024-02-16-frida-16-2-1-released.markdown b/_i18n/cn/_posts/2024-02-16-frida-16-2-1-released.markdown new file mode 100644 index 00000000..6a19671d --- /dev/null +++ b/_i18n/cn/_posts/2024-02-16-frida-16-2-1-released.markdown @@ -0,0 +1,15 @@ +--- +layout: news_item +title: 'Frida 16.2.1 发布' +date: 2024-02-16 21:19:46 +0100 +author: oleavr +version: 16.2.1 +categories: [release] +--- + +只是一个快速的错误修复版本,用于处理影响 palera1n (rootless) 和 Dopamine 用户的 iOS 打包错误。感谢 [@miticollo][] 如此迅速地报告此问题。 + +享受吧! + + +[@miticollo]: https://github.com/miticollo diff --git a/_i18n/cn/_posts/2024-05-17-frida-16-2-2-released.markdown b/_i18n/cn/_posts/2024-05-17-frida-16-2-2-released.markdown new file mode 100644 index 00000000..1c08ee49 --- /dev/null +++ b/_i18n/cn/_posts/2024-05-17-frida-16-2-2-released.markdown @@ -0,0 +1,167 @@ +--- +layout: news_item +title: 'Frida 16.2.2 发布' +date: 2024-05-17 18:26:11 +0200 +author: oleavr +version: 16.2.2 +categories: [release] +--- + +现在是发布时间,这个版本充满了改进!🎉 + +## 构建系统 + +在对我们的构建系统状况不满意多年之后,我终于鼓起勇气重组它——以一种大的方式。 + +### 用户体验 + +困扰我的一件事是,我们的构建系统感觉非常古怪和复杂。你会调用 *make* 并看到你可以构建的东西的菜单。但是该菜单会根据你运行它的操作系统而有所不同。更改构建选项意味着编辑 config.mk 或在命令行上覆盖它们。如果你在 Windows 上,你必须使用 Visual Studio (MSVS)。 + +MSVS 构建系统现在已经消失了,你可以像构建许多其他 OSS 项目一样构建 Frida: + +{% highlight bash %} +$ ./configure +$ make +{% endhighlight %} + +如果你不需要传递任何选项(例如 --prefix),则可以跳过 *configure* 步骤。这个脚本实际上只是 Meson setup 命令的一个薄包装器,你可以通过添加 `--` 后跟选项将选项直接传递给 Meson。你也可以运行 *make install* 来安装东西,它支持 DESTDIR 环境变量来更改输出的位置。 + +交叉编译也很容易;例如,如果你想为 iOS/arm64 构建,请使用 --host=ios-arm64 调用 configure。如果你有嵌入式系统的工具链,请传递 --host=$triplet,它将在你的 PATH 上查找 $triplet-gcc, $triplet-g++ 等。你还可以将 CFLAGS, LDFLAGS 等添加到环境中以传递额外的标志,就像你期望从其他构建系统那样。 + +很酷的是,我们不同的 repo 现在可以独立构建。你可以克隆 frida-gum repo 并构建它,就像你可以对 frida-python, frida-tools 等做的那样。frida repo 不再那么重要,它只是为了能够对一组 repo 进行版本控制,并托管用于滚动新版本的 CI。 + +每个 repo 包含任何需要的 subprojects/*.wrap 文件,告诉 Meson 在哪里可以找到依赖项。这意味着如果你获取 frida-core 并尝试构建它,而你的 PKG_CONFIG_PATH 上还没有 frida-gum,它会自动为你获取并构建它。同样整洁的是,我们的 Python 和 Node.js 绑定现在利用这一点从源代码构建,如果找不到或无法下载 .whl 或预构建。 + +所以现在我们不同的组件作为子项目运行良好,这也意味着任何人都可以同样轻松地将 Frida 组件集成到他们自己的项目中。你所要做的就是在项目顶级源目录中的 subprojects/ 添加一个 .wrap-file,并从你的 meson.build 调用 dependency()。Meson 然后首先在你的系统上查找,如果找不到依赖项,它将克隆 subprojects/ 中的 git repo 并将其作为项目的一部分进行构建。也可以告诉 Meson 强制这种回退而不查看你的系统。 + +这是 Gum 的 .wrap-file 可能的样子: + +{% highlight ini %} +[wrap-git] +url = https://github.com/frida/frida-gum.git +revision = 16.2.4 +depth = 1 + +[provide] +dependency_names = frida-gum-1.0, frida-gum-heap-1.0, frida-gum-prof-1.0, frida-gumjs-1.0, frida-gumjs-inspector-1.0 +{% endhighlight %} + +对于 frida-core: + +{% highlight ini %} +[wrap-git] +url = https://github.com/frida/frida-core.git +revision = 16.2.4 +depth = 1 + +[provide] +dependency_names = frida-core-1.0 +{% endhighlight %} + +我们的薄 Meson 包装器也很容易上手,你可以在 [here][] 阅读更多相关信息。 + +### 一点历史 + +我选择为 Windows 维护单独的构建系统的原因是,我以前与相当多经验丰富的 Windows 开发人员合作过,并注意到如果他们可以使用自己喜欢的 IDE 进行工作,他们会对 OSS 项目更感兴趣。对他们来说重要的是,他们可以在调试器中查看崩溃,跳转到属于 OSS 库的帧,并能够添加一些临时日志代码。然后按“运行”热键让 IDE 增量编译并重新链接所有内容,以获得简短而甜蜜的反馈循环。 + +当时,Frida 的非 Windows 构建系统是 autotools。我们后来搬到了 Meson。虽然 Meson 有一个 MSVS 后端,这意味着我们可以让它为我们生成 MSVS 项目文件,但有一个问题阻止了我们这样做。与 MSVS 不同,你可以在同一个“解决方案”(工作区)中混合针对不同机器的项目,Meson 一次只支持为一台机器编译,除了构建机器。由于 Frida 支持将代码注入例如 Windows 上的 32 位和 64 位目标,如果我们删除 MSVS 构建系统,我们将引入可用性回归。所以因为这个我犹豫要不要放弃 MSVS 构建系统。 + +我们在非 Windows 上也遇到了同样的挑战。我最终通过编写一个 Makefile 来解决它,该 Makefile 为每个架构调用 Meson,将它们粘合在一起。虽然这确实有效,但我从未让它达到增量构建也能可靠工作的程度。然而,今年 3 月下旬,我终于确定了一种在 frida-core 中本地解决此问题的方法——这是我们需要这种多架构疯狂的唯一组件。通过从 custom_target() 递归调用 Meson,我们可以为其他架构构建所需的组件。 + +这花了很多工作才使其正常工作,但一旦到了那里,它让堆栈其余部分的工作变得更加愉快。*frida* repo 的 Makefile 和 shell 脚本可以被删除并替换为 Meson。CI 变得更简单、更统一。任何人都可以克隆 frida-core repo 并运行 *make*,生成的二进制文件将支持跨架构。(除非通过 --disable-compat / -Dfrida-core:compat=disabled 显式禁用。) + +现在你可能想知道为什么我们还在运行 *make*,如果我们现在只使用 Meson。我最终在 Meson 周围编写了一个薄包装器,它可以自动下载预构建的依赖项,为较小的二进制文件选择合理的链接器标志等。每个 repo 中的新 *configure* 和 *Makefile* 文件负责调用 releng/meson_configure.py 和 releng/meson_make.py,它们构成了 Meson 周围的薄包装器。*releng* 目录是一个子模块,由 Frida 的 repo 共享。在调用包装器之前做的另一件事是确保 releng 子模块已被初始化和更新。 + +### 维护 + +在此版本之前,我们维护着七个不同的构建系统: + +1. Meson (主要组件,在非 Windows 上) +2. Visual Studio (主要组件,在 Windows 上) +3. GNU Make (主要组件元构建系统,在非 Windows 上) +4. setuptools (Python 绑定) +5. GYP (Node.js 绑定) +6. Xcode (Swift 绑定) +7. QMake (QML 绑定) + +截至此版本,我很高兴地宣布我们只剩下一个构建系统:Meson。 + +以前情况的主要痛点是必须同时处理 1) 和 2),因为它们涉及所有主要组件。这意味着像添加源文件这样简单的事情需要了解两个不同的构建系统。不仅如此,例如 Linux 上的贡献者通常很难测试他们的 Visual Studio 构建系统更改。 + +另一个方面是,如果像添加新文件这样简单的事情涉及处理两个构建系统,开发人员就不太愿意重构他们的代码。这的长期后果是它助长了坏习惯。“嗯,我应该把它拆分成一个单独的文件,但是呃……不,那太痛苦了,我现在要把代码加在这里。” + +至于剩下的构建系统,仅用于绑定,维护它们似乎并没有那么糟糕——反正接触该代码的人通常都很熟悉它们。那里的挑战是除了其中一个 (QMake) 之外,所有这些都缺乏 pkg-config 集成。这意味着包含路径、库路径和要链接的库最终会在多个地方重复。当链接使用内部其他库的静态库时,这尤其棘手,这通常是我们的语言绑定使用 frida-core 的方式。 + +## 新功能 + +此版本中有相当多令人兴奋的新事物: + +- gumjs: 添加 Process.runOnThread(),以便轻松在特定线程上运行任意代码。必须小心使用以避免死锁/重入问题。 +- gumjs: 向 Thread.backtrace() 添加 *limit* 选项。感谢 [@davinci-tech][]! +- gumjs: 使用已弃用 API 的替代方案扩展 CModule glib.h。 +- stalker: 添加 StalkerIterator.put_chaining_return()。感谢 [@s1341][]! +- stalker: 添加 run_on_thread() 和 run_on_thread_sync()。 +- interceptor: 添加对 x86 上影子堆栈的支持。感谢 [@yjugl][]! +- cpu-features: 添加 CET_SS 标志和检测逻辑。感谢 [@yjugl][]! +- x86-writer: 添加 cpu_features 字段。感谢 [@yjugl][]! +- spinlock: 添加 try_acquire()。感谢 [@mrmacete][]! +- cloak: 添加 with_lock_held() 和 is_locked()。感谢 [@mrmacete][]! +- interceptor: 添加 with_lock_held() 和 is_locked()。感谢 [@mrmacete][]! +- darwin: 当助手崩溃时提示 macOS 引导参数。 +- java: 支持实例化没有构造函数的类。感谢 [@AeonLucid][]! +- java: 添加对数组的数组的支持。感谢 [@histausse][]! +- python: 使源代码分发完全可以从源代码构建,而不是仅支持使用 devkit 构建。 +- python: 放弃 MSVS 构建系统。 +- node: 添加 Meson 构建系统,放弃 prebuild 和 node-gyp 位。 +- node: 支持在找不到预构建时完全从源代码构建。 +- clr: 添加 Meson 构建系统,放弃 MSVS 构建系统。 +- qml: 添加 Meson 构建系统,放弃 qmake 构建系统。 +- qml: 添加对 Qt 6 的支持。感谢 [@zaxo7][]! +- qml: 放弃对 Qt 5 的支持。 + +## 错误修复 + +最后但并非最不重要的一点是,我们还为您带来了一长串质量改进: + +- gumjs: 在 NativeCallback 调用中保留线程的系统错误。感谢 [@HexKitchen][]! +- gumjs: 始终向 NativeCallback 公开线程的系统错误。 +- gumjs: 堵塞 Stalker 实例的泄漏。 +- stalker: 修复 arm64 上破坏独占访问的块事件。感谢 [@saicao][]! +- memory: 修复 RWX 系统上的 patch_code() 保护翻转。 +- swift-api-resolver: 修复间接类型条目的处理。 +- swift: 解决 iOS 模拟器上的 Module.load() 问题。感谢 [@zydeco][]! +- base: 修复自定义 GSource 实现中的竞争性崩溃。 +- base: 修复 p2p AgentSession 注册逻辑。 +- buffer: 修复 read_string() 大小逻辑。感谢 [@hsorbo][]! +- linux: 修复某些 32 位系统上的早期插桩。 +- linux: 修复现代 Android 上的 inject_library_blob()。 +- linux: 修复不可靠的 exec 过渡逻辑。 +- linux: 修复 libc 缺失时不可靠的注入。 +- agent: 修复 child-gating fork() 场景中的挂起。在 pidfd_getfd() 可用但不允许的情况下可重现,例如在 Docker 容器内。 +- windows: 使用 RW/RX 权限进行注入。这使得 Frida 注入与更多软件兼容。特别是,如果起始地址是 RWX,Mozilla Firefox 会拒绝线程启动。感谢 [@yjugl][]! +- darwin: 在 Frida hook 周围按摩 libunwind,以避免破坏使用异常的代码,例如通过 Objective-C 中的 @try/@catch。感谢 [@mrmacete][]! +- darwin: 在 ThreadSuspendMonitor 中获取 Interceptor 和 Cloak 锁,以扩展其范围以防止持有 Cloak 或 Interceptor 锁的线程被挂起的死锁情况。感谢 [@mrmacete][]! +- darwin: 修复 InjectInstance 调度源的竞争性拆卸。 +- darwin: 修复 SpawnInstance 调度源的竞争性拆卸。 +- android: 修复 execl() 及其朋友的 child-gating。 +- compiler: 升级 @types/frida-gum。感谢 [@s1341][]! +- modulate: 优雅地处理丢失的符号。感谢 [@hsorbo][]! +- python: 将 _frida 包移动到 frida 包内。 + +## EOF + +这几乎总结了它。享受吧! + + +[here]: https://github.com/frida/releng?tab=readme-ov-file#setting-up-a-new-project +[@davinci-tech]: https://github.com/davinci-tech +[@s1341]: https://github.com/s1341 +[@yjugl]: https://github.com/yjugl +[@mrmacete]: https://twitter.com/bezjaje +[@AeonLucid]: https://twitter.com/AeonLucid +[@histausse]: https://github.com/histausse +[@zaxo7]: https://github.com/zaxo7 +[@HexKitchen]: https://github.com/HexKitchen +[@saicao]: https://github.com/saicao +[@zydeco]: https://github.com/zydeco +[@hsorbo]: https://twitter.com/hsorbo diff --git a/_i18n/cn/_posts/2024-05-18-frida-16-2-3-released.markdown b/_i18n/cn/_posts/2024-05-18-frida-16-2-3-released.markdown new file mode 100644 index 00000000..7d08aa0a --- /dev/null +++ b/_i18n/cn/_posts/2024-05-18-frida-16-2-3-released.markdown @@ -0,0 +1,17 @@ +--- +layout: news_item +title: 'Frida 16.2.3 发布' +date: 2024-05-18 23:05:11 +0200 +author: oleavr +version: 16.2.3 +categories: [release] +--- + +只是一个快速的错误修复版本,用于 [fix][] 影响 iOS 用户的回归。感谢 [@andyacer][] 和 [@IhorLeshko][] 如此迅速地报告此问题。 + +享受吧! + + +[fix]: https://github.com/frida/frida-core/commit/5e149a3f2df5cb4ea8fe46e805c39e5967957d0a +[@andyacer]: https://github.com/andyacer +[@IhorLeshko]: https://github.com/IhorLeshko diff --git a/_i18n/cn/_posts/2024-05-20-frida-16-2-4-released.markdown b/_i18n/cn/_posts/2024-05-20-frida-16-2-4-released.markdown new file mode 100644 index 00000000..bde88993 --- /dev/null +++ b/_i18n/cn/_posts/2024-05-20-frida-16-2-4-released.markdown @@ -0,0 +1,15 @@ +--- +layout: news_item +title: 'Frida 16.2.4 发布' +date: 2024-05-20 22:54:38 +0200 +author: oleavr +version: 16.2.4 +categories: [release] +--- + +只是一个快速的错误修复版本,用于修复 Windows 上 DebugSymbol API 中的回归,我们未能捆绑 DbgHelp 和 SymSrv 库。感谢 [0xDC00][] 如此迅速地报告此问题。 + +享受吧! + + +[0xDC00]: https://github.com/0xDC00 diff --git a/_i18n/cn/_posts/2024-05-21-frida-16-2-5-released.markdown b/_i18n/cn/_posts/2024-05-21-frida-16-2-5-released.markdown new file mode 100644 index 00000000..25206813 --- /dev/null +++ b/_i18n/cn/_posts/2024-05-21-frida-16-2-5-released.markdown @@ -0,0 +1,19 @@ +--- +layout: news_item +title: 'Frida 16.2.5 发布' +date: 2024-05-21 12:50:43 +0200 +author: oleavr +version: 16.2.5 +categories: [release] +--- + +一个快速的错误修复版本,包含三个改进: + +- ci: 修复 macOS 的 frida-node 预构建循环,以便我们为所有目标生成预构建,而不仅仅是第一个。 +- node: 避免依赖 package-lock.json,以支持在缺少预构建时的回退构建。 +- android: 在 Java.registerClass() 中将 DexFile 设置为只读。从 Android 14 开始,targetSdk >= 34 的应用程序不允许对动态加载的 Dex 文件具有可写权限。感谢 [@pandasauce][]! + +享受吧! + + +[@pandasauce]: https://github.com/pandasauce diff --git a/_i18n/cn/_posts/2024-05-31-frida-16-3-0-released.markdown b/_i18n/cn/_posts/2024-05-31-frida-16-3-0-released.markdown new file mode 100644 index 00000000..d2ca92b7 --- /dev/null +++ b/_i18n/cn/_posts/2024-05-31-frida-16-3-0-released.markdown @@ -0,0 +1,320 @@ +--- +layout: news_item +title: 'Frida 16.3.0 发布' +date: 2024-05-31 15:43:02 +0200 +author: oleavr +version: 16.3.0 +categories: [release] +--- + +此版本包含一些令人兴奋的新事物。让我们直接潜入。 + +## CoreDevice + +从 iOS 17 开始,Apple 转向了一种与 iDevice 端服务通信的新方式,包括他们的 Developer Disk Image (DDI) 服务。他们做事的新方式统一了 Apple 软件与其他 Apple 设备对话的方式,被称为 CoreDevice。它似乎起源于 Apple 推出他们的 T2 协处理器时。Cisco 的 DUO 团队早在 2019 年就对此发表了一些出色的 [research][]。 + +与 T2 一样,用于 iOS 的新堆栈也使用 RemoteXPC 与服务对话。不过事情稍微复杂一些,因为与 T2 不同,移动设备没有与 macOS 的持久连接,并且还有配对的概念。幸运的是,[@doronz88][] 已经做了一项了不起的工作,逆向并 [documenting][] 了我们需要实现新协议的大部分内容,这为我们节省了大量时间。 + +即便如此,这也是一项艰巨的任务,但也非常有趣——[@hsorbo][] 和我在结对编程中玩得很开心。涉及相当多的活动部件,让我们快速浏览一下。 + +插入 iDevice 后,它会公开一个 USB CDC-NCM 网络接口,称为“专用”接口。它还公开了一个用于网络共享的接口,就像过去一样——除了那时它是使用专有的 Apple 协议而不是 CDC-NCM。 + +这就是我们在尝试从 Linux 主机与其对话时遇到的第一个挑战。首先,iOS 设备需要一个 USB 供应商请求来进行模式切换,以公开新接口。这部分很容易,因为如果我们设置环境变量 `USBMUXD_DEFAULT_DEVICE_MODE` 为 `3`,usbmuxd 守护进程可以为我们做这件事。到目前为止一切顺利。 + +下一个挑战是 Linux 内核的 CDC-NCM 驱动程序无法绑定到设备。经过一番调试,我们发现这是由于专用网络接口缺少状态端点。状态端点是驱动程序获取有关网络电缆是否插入的通知的方式。Apple 的网络共享网络接口有这样一个端点,这很有意义——如果禁用网络共享,就像电缆被拔掉一样。但是专用接口总是存在的,所以可以理解 Apple 选择不为其包含状态端点。 + +我们迅速开发了一个 [kernel driver patch][] 来取消对状态端点的要求,这让它工作了。后来我们意识到我们仍然应该要求网络共享接口具有状态端点,所以我们最终进一步完善了我们的补丁。计划是在接下来的几天内提交。 + +无论如何,随着网络接口的启动,主机端使用 mDNS 来定位 RemoteServiceDiscovery (RSD) 服务正在侦听的 IPv6 地址。主机连接到它并使用 HTTP/2 进行通信,RemoteXPC 消息来回传递。这个特定的服务 RSD 告诉主机专用接口上有哪些服务可用,它们正在侦听的端口号,以及每个服务用于通信的协议等详细信息。 + +然后,知道哪些服务正在侦听哪些端口,主机查找 Tunnel 服务。此服务允许主机建立到设备的隧道,充当 VPN 以允许其与该隧道内的服务通信。由于建立这样的隧道需要主机和设备之间的配对关系,这意味着隧道内的服务允许主机做比隧道外的服务更多的事情。 + +基本协议与 RSD 相同,在涉及配对参数二进制 blob 和一些加密技术的来回之后,配对关系被创建或验证。此时两个端点正在使用加密通信,主机要求 Tunnel 服务设置隧道侦听器。 + +现在,假设主机请求默认传输 QUIC,主机继续连接到它。我们应该注意,Tunnel 服务也支持纯 TCP。据推测,这是为不带 QUIC 堆栈的旧 macOS 版本准备的。另一件值得一提的事情是,Tunnel 服务为主机提供了一个密钥对,因此它将其用作连接设置的一部分。 + +连接后,设备通过可靠流向主机发送一些数据。数据以 8 字节魔术字“CDTunnel”开头,后跟一个指定其后有效载荷大小的大端 uint16。有效载荷是 JSON,告诉主机主机在隧道内的端点具有哪个 IPv6 地址,以及网络掩码和 MTU。它还告诉主机其自己在隧道内的 IPv6 地址,以及 RSD 服务正在侦听的端口。 + +然后主机设置一个按刚刚告知的方式配置的 TUN 设备,并开始将从 QUIC 连接接收到的不可靠数据报馈送给它。对于另一个方向的数据,每当有来自 TUN 设备的新数据包时,主机将其馈送到 QUIC 连接中。 + +所以此时主机连接到隧道内的 RSD 端点,从那里它可以访问设备提供的所有服务。这种新方法的美妙之处在于,与设备端服务通信的客户端不必担心加密,也不必提供配对关系的证明。它们只需在隧道接口上进行纯文本 TCP 连接,QUIC 透明地处理其余部分。 + +更酷的是,主机可以通过 USB 和 WiFi 建立隧道,并且由于 QUIC 对多路径的本机支持,设备可以在有线和无线之间无缝移动,而不会中断与隧道内服务的连接。 + +所以,一旦我们实现了所有这些,我们就感到兴奋和乐观。剩下的部分就是做平台集成。呃,是的,那是事情变得更加困难的地方。让我们卡住了一段时间的部分是在 macOS 上,我们意识到我们必须搭载 Apple 现有的隧道。我们发现当已经有一个隧道打开时,Tunnel 服务会拒绝与我们交谈,所以我们不能简单地在 Apple 的隧道旁边打开一个隧道。 + +虽然我们可以要求用户向 `remoted` 发送 SIGSTOP,允许我们建立自己的隧道,但这不会是一个很好的用户体验。特别是因为任何想要与设备交谈的 Apple 软件都将无法交谈,这使得 Xcode、Console 等变得不那么有用。 + +没过多久我们就找到了允许我们确定 Apple 隧道内的设备端地址的私有 API,并且还创建了一个所谓的“断言”,以便隧道在我们需要的尽可能长的时间内保持打开状态。但是我们无法弄清楚的部分是如何发现隧道内的设备端 RSD 端口。 + +我们知道作为本地用户运行的 `remotepairingd` 知道 RSD 端口,但我们找不到让它告诉我们是什么的方法。经过大量的头脑风暴,我们只能想到不切实际的解决方案: + +- 端口扫描设备端地址:可能很慢,更快的实现需要 root 权限才能进行原始套接字访问。 +- 扫描 `remotepairingd` 的地址空间以查找设备端隧道地址,并定位存储在其附近的端口:启用 SIP 时不起作用。 +- 依赖设备端 frida-server 为我们弄清楚事情:这在 jailed iOS 上不起作用,并且会很复杂且可能很脆弱。 +- [Grab it from the syslog][]: 可能需要一段时间或需要手动用户操作,并且通过杀死系统守护进程强制重新连接会导致中断。 +- 放弃使用隧道,并转移到更高级别的抽象,例如每当我们需要打开服务时使用 MobileDevice.framework:需要我们拥有 entitlements。具体取决于特定的服务。例如,如果我们想与 com.apple.coredevice.appservice 交谈,我们需要 com.apple.private.CoreDevice.canInstallCustomerContent entitlement。但是,试图给自己一个 com.apple.private.* entitlement 是行不通的,因为系统会杀死我们,因为只有 Apple 签名的程序才能使用此类 entitlements。 + +这是我们决定休息一下并专注于其他事情一段时间的地方,直到我们最终找到一种方法:`remoted` 进程与隧道内的 RSD 服务有一个连接。我们最终得到了一个简单的解决方案,使用 Apple 的 netstat 使用的相同 API: + +{% highlight vala %} +foreach (var item in XNU.query_active_tcp_connections ()) { + if (item.family != IPV6) + continue; + if (!item.foreign_address.equal (tunnel_device_address)) + continue; + if (Darwin.XNU.proc_pidpath (item.effective_pid, path_buf) <= 0) + continue; + if (path != "/usr/libexec/remoted") + continue; + + try { + var connectable = new InetSocketAddress (tunnel_device_address, item.foreign_port); + + var sc = new SocketClient (); + SocketConnection connection = yield sc.connect_async (connectable, cancellable); + Tcp.enable_nodelay (connection.socket); + + return yield DiscoveryService.open (connection, cancellable); + } catch (GLib.Error e) { + } +} +{% endhighlight %} + +不过,Linux 方面的情况要容易得多,因为我们在那里处于控制之中,可以自己建立隧道。但是有一个挑战:我们不想要求提升的权限才能创建 tun 设备。我们想出的解决方案是使用 [lwIP][] 在用户模式下执行 IPv6。由于我们已经设计了其他构建块以与 GLib.IOStream 一起工作,与套接字和网络分离,我们要做的就是实现一个使用 lwIP 来完成繁重工作的 IOStream。来自 QUIC 连接的数据报被馈送到 lwIP 网络接口,该网络接口发出的数据包作为数据报馈送到 QUIC 连接中。 + +接下来是 Windows 方面的事情。我们做了一些挖掘,很快意识到 Apple 的软件目前没有建立隧道。官方驱动程序似乎也让 USB 设备保持忙碌,这意味着我们无法轻松触发模式切换并自己做事。拼图的 Windows 部分可能有一个优雅的解决方案,但我们意识到我们已经深陷兔子洞,明智的做法是留待以后解决。所以如果正在阅读本文的任何人有兴趣提供帮助,请务必联系我们。 + +另一个需要未来改进的领域是我们仅支持 macOS 上的有线连接。一旦我们更新了 frida-server 和 frida-gadget,以便它们在隧道接口出现时侦听它们,这应该很容易改进。 + +## Jailed iOS 17 + +随着新的 CoreDevice 基础设施到位,我们还恢复了对 iOS 17 上 jailed 检测的支持。这意味着我们可以再次在最新的 iOS(撰写本文时为 17.5.1)上 spawn(可调试)应用程序。我们仍然存在没有 spawn() 的 attach() 不起作用的问题,因为我们的 jailed 注入器在附加到已经在运行的应用程序时尚不支持可重启的 dyld 情况。这将在未来的版本中解决。 + +## Device.open_service() 和 Service API + +鉴于 Frida 需要讲相当多的协议才能与 Apple 的设备端服务交互,并且应用程序有时也需要其他此类服务,这就提出了一个挑战。应用程序可以自己讲这些协议,例如在使用 Device.open_channel() API 打开到特定服务的 IOStream 之后。但这这意味着它们必须重复实现和维护协议栈的工作,对于像 DTX 这样的协议,它们可能会浪费时间建立 Frida 已经为其自身需求建立的连接。 + +一种可能的解决方案是将这些服务客户端设为公共 API 并在我们的语言绑定中公开它们。我们还必须为应用程序可能想要与之交谈的所有 Apple 服务实现客户端。这相当多,会导致 Frida API 变得庞大。这也会使 Frida 成为某种大杂烩,这显然不是我们想要前进的方向。 + +在思考了一段时间后,我想到我们可以提供一个通用的抽象,让应用程序与它们想要的任何服务交谈。所以上周 [@hsorbo][] 和我倒满咖啡就开始着手做这件事。 + +这是从 Python 与 RemoteXPC 服务交谈是多么容易: + +{% highlight py %} +import frida +import pprint + +device = frida.get_usb_device() + +appservice = device.open_service("xpc:com.apple.coredevice.appservice") +response = appservice.request({ + "CoreDevice.featureIdentifier": "com.apple.coredevice.feature.listprocesses", + "CoreDevice.action": {}, + "CoreDevice.input": {}, +}) +pprint.pp(response) +{% endhighlight %} + +同样的例子,来自 Node.js: + +{% highlight js %} +import frida from 'frida'; +import util from 'util'; + +const device = await frida.getUsbDevice(); + +const appservice = await device.openService('xpc:com.apple.coredevice.appservice'); +const response = await appservice.request({ + 'CoreDevice.featureIdentifier': 'com.apple.coredevice.feature.listprocesses', + 'CoreDevice.action': {}, + 'CoreDevice.input': {}, +}); +console.log(util.inspect(response, { + colors: true, + depth: Infinity, + maxArrayLength: Infinity +})); +{% endhighlight %} + +结果是: + +![open-service-xpc.png](/img/open-service-xpc.png "iOS process list") + +既然我们已经看了从 Python 和 Node.js 使用的新 open_service() API,我们可能应该提到从 C 使用此 API(几乎)同样容易: + +{% highlight c %} +#include + +int +main (int argc, + char * argv[]) +{ + GCancellable * cancellable = NULL; + GError * error = NULL; + + frida_init (); + + FridaDeviceManager * manager = frida_device_manager_new (); + + FridaDevice * device = frida_device_manager_get_device_by_type_sync (manager, FRIDA_DEVICE_TYPE_USB, -1, cancellable, &error); + FridaService * service = frida_device_open_service_sync (device, "xpc:com.apple.coredevice.appservice", cancellable, &error); + + GVariant * parameters = g_variant_new_parsed ("{" + "'CoreDevice.featureIdentifier': <'com.apple.coredevice.feature.listprocesses'>," + "'CoreDevice.action': <@a{sv} {}>," + "'CoreDevice.input': <@a{sv} {}>" + "}"); + GVariant * response = frida_service_request_sync (service, parameters, cancellable, &error); + + gchar * str = g_variant_print (response, FALSE); + g_printerr ("%s\n", str); + + return 0; +} +{% endhighlight %} + +(为简洁起见省略了错误处理和清理。) + +传递给 `open_service()` 的字符串是可以访问服务的地址,它以协议标识符开头,后跟冒号和服务名称。这将返回 Service 接口的实现,如下所示: + +{% highlight vala %} +public interface Service : Object { + public signal void close (); + public signal void message (Variant message); + + public abstract bool is_closed (); + public abstract async void activate (Cancellable? cancellable = null) throws Error, IOError; + public abstract async void cancel (Cancellable? cancellable = null) throws IOError; + public abstract async Variant request (Variant parameters, Cancellable? cancellable = null) throws Error, IOError; +} +{% endhighlight %} + +(为简洁起见省略了同步方法。) + +在这里,`request()` 是您使用 Variant 调用的方法,它可以是“任何东西”,字典、数组、字符串等。期望什么取决于特定协议。我们的语言绑定负责将本机值(例如 Python 中的 dict)转换为 Variant。一旦 `request()` 返回,它会为您提供一个带有响应的 Variant。然后将其转换为本机值,例如 Python dict。 + +对于支持通知的协议,每当收到通知时都会发出 `message` 信号。由于 Frida 的 API 还提供所有方法的同步版本,允许从任意线程调用它们,这提出了一个挑战:如果您在打开特定服务后立即发出消息,您可能来不及注册处理程序。这就是 `activate()` 发挥作用的地方。服务对象开始处于非活动状态,允许注册信号处理程序。然后,一旦您准备好接收事件,您可以调用 `activate()`,或者进行 `request()`,这会将服务对象移至活动状态。 + +然后,稍后,要关闭事物,可以调用 `cancel()`。`close` 信号对于知道 Service 何时不再可用很有用,例如因为设备被拔掉或者您向其发送了无效消息导致其关闭连接。 + +与 DTX 服务交谈也很容易,它是 RemoteXPC 的前身,仍被许多 DDI 服务使用。 + +例如,要抓取屏幕截图: + +{% highlight py %} +import frida + +device = frida.get_usb_device() + +screenshot = device.open_service("dtx:com.apple.instruments.server.services.screenshot") +png = screenshot.request({"method": "takeScreenshot"}) +with open("/path/to/outfile.png", "wb") as f: + f.write(png) +{% endhighlight %} + +但这还不是全部。我们还支持与旧式 plist 服务交谈,您发送 plist 作为请求,并接收一个或多个 plist 响应: + +{% highlight py %} +import frida + +device = frida.get_usb_device() + +diag = device.open_service("plist:com.apple.mobile.diagnostics_relay") +diag.request({"type": "query", "payload": {"Request": "Sleep", "WaitForDisconnect": True}}) +diag.request({"type": "query", "payload": {"Request": "Goodbye"}}) +{% endhighlight %} + +正如您可能已经猜到的那样,此示例使连接的 iDevice 进入睡眠状态。 + +## RPC 二进制传递 + +使用 Gum 的 JavaScript 绑定的人可能熟悉我们的 RPC API,它使应用程序可以轻松调用其代理上的函数。 + +此功能的一个长期限制是您无法将二进制数据传递到导出函数中——您必须以某种方式对其进行序列化。这现在终于得到支持。 + +例如,给定以下代理: + +{% highlight js %} +rpc.exports.hello = (name, icon) => { + console.log(`Name: "${name}"`); + console.log('Icon:'); + console.log(hexdump(icon, { ansi: true })); +}; +{% endhighlight %} + +您现在可以像这样从 Python 调用 `hello()`: + +{% highlight js %} +script.exports_sync.hello("Joe", b"\x13\x37") +{% endhighlight %} + +这将输出: + +![rpc-binary-parameter.png](/img/rpc-binary-parameter.png "RPC binary parameter demo") + +请注意,只能传递一个二进制参数,并且它必须是最后一个参数。 + +同一领域的另一个改进是,您现在可以将二进制数据与 JSON 可序列化的 JavaScript 值一起返回。我们以前只支持返回其中之一。这现在也终于得到支持。 + +例如,给定以下代理: + +{% highlight js %} +rpc.exports.peek = name => { + const module = Process.getModuleByName(name); + const header = module.base.readByteArray(64); + return [module, header]; +}; +{% endhighlight %} + +您现在可以像这样从 Python 调用 `peek()`: + +{% highlight js %} +module, header = script.exports_sync.peek("libSystem.B.dylib") +print("module:", module) +print("header:", header) +{% endhighlight %} + +如果运行在存在 libSystem.B.dylib 的平台上,可能会输出如下内容: + +{% highlight sh %} +module: {'name': 'libSystem.B.dylib', 'base': '0x18ec70000', 'size': 8192, 'path': '/usr/lib/libSystem.B.dylib'} +header: b'\xcf\xfa\xed\xfe\x0c\x00\x00\x01\x02\x00\x00\x80\x06\x00\x00\x006\x00\x00\x00\x10\x10\x00\x00\x85\x00\x00\x82\x00\x00\x00\x00\x19\x00\x00\x00(\x02\x00\x00__TEXT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x0c\x8d\x01\x00\x00\x00' +{% endhighlight %} + +## EOF + +还有一些其他令人兴奋的更改,所以一定要查看下面的变更日志。 + +享受吧! + +## 变更日志 + +- 添加 device.open_service(address),它提供了一个 Service 实现,通过统一接口公开特定于设备的服务。 +- fruity: 添加 RemoteXPC 支持,为三个协议 ID 实现了 open_service():plist, dtx, xpc。 +- fruity: 修复 jailed iOS 17 上的 spawn()。 +- fruity: 改进 DTX 协议支持。 +- python: 添加 open_service() 和 Service API。 +- python: 添加对新 RPC 二进制数据传递的支持。 +- python: 改进 GVariant 编组支持。 +- node: 添加 openService() 和 Service API。 +- node: 添加对新 RPC 二进制数据传递的支持。 +- node: 改进 GVariant 编组支持。 +- exceptor: 在 POSIX 后端添加 SA_ONSTACK 标志。感谢 [@asabil][]! +- gumjs: 支持在 RPC 方法中接收二进制数据。 +- gumjs: 支持从 RPC 方法返回数值和二进制数据。 +- gumjs: 向 CModule 公开更多 Memory API。感谢 [@hillelpinto][]! +- plist: 修复对写出带有 float 和 double 值的二进制 plist 的支持。 + +感谢 [@hsorbo][] 在上述所有更改中进行有趣且富有成效的结对编程,没有具体归属!🙌 + + +[research]: https://duo.com/labs/research/apple-t2-xpc +[@doronz88]: https://github.com/doronz88 +[documenting]: https://github.com/doronz88/pymobiledevice3/blob/master/misc/RemoteXPC.md +[@hsorbo]: https://twitter.com/hsorbo +[kernel driver patch]: https://lore.kernel.org/all/20231130220109.90734-2-oleavr@frida.re/T/#ma666f28da8f071f3433e915a28b2152d08373de5 +[Grab it from the syslog]: https://github.com/doronz88/pymobiledevice3/blob/master/misc/RemoteXPC.md#reusing-the-macos-trusted-tunnel +[lwIP]: https://savannah.nongnu.org/projects/lwip/ +[@asabil]: https://twitter.com/asabil +[@hillelpinto]: https://github.com/hillelpinto diff --git a/_i18n/cn/_posts/2024-05-31-frida-16-3-1-released.markdown b/_i18n/cn/_posts/2024-05-31-frida-16-3-1-released.markdown new file mode 100644 index 00000000..4f013c8e --- /dev/null +++ b/_i18n/cn/_posts/2024-05-31-frida-16-3-1-released.markdown @@ -0,0 +1,15 @@ +--- +layout: news_item +title: 'Frida 16.3.1 发布' +date: 2024-05-31 22:52:35 +0200 +author: oleavr +version: 16.3.1 +categories: [release] +--- + +只是一个快速的错误修复版本,旨在解决我们的 Node.js 绑定的打包问题。上传的预构建版本没有被剥离,并且导出了它们本不该导出的符号。感谢 [0xDC00][] 的报告。 + +享受吧! + + +[0xDC00]: https://github.com/0xDC00 diff --git a/_i18n/cn/_posts/2024-06-04-frida-16-3-2-released.markdown b/_i18n/cn/_posts/2024-06-04-frida-16-3-2-released.markdown new file mode 100644 index 00000000..adf6ea4d --- /dev/null +++ b/_i18n/cn/_posts/2024-06-04-frida-16-3-2-released.markdown @@ -0,0 +1,29 @@ +--- +layout: news_item +title: 'Frida 16.3.2 发布' +date: 2024-06-04 13:41:24 +0200 +author: oleavr +version: 16.3.2 +categories: [release] +--- + +是时候发布另一个错误修复版本了。这里面有不少好东西: + +- darwin: 在注入期间避免 thread_set_state(),这样我们就不会在例如 macOS >= 14.5 上被系统杀死。感谢 [@\_saagarjha][] 贡献了修复的初稿! +- fruity: 对需要它的服务执行 RSDCheckin。通过这种方式,当 CoreDevice 隧道可用时,我们保留了 lockdown 服务的 open_channel() 的向后兼容性。 +- fruity: 停止缓存 LockdownClient,以避免多个消费者的问题。使用连接了 jailed iOS 设备的 frida-ps 可重现。 +- python: 修复 open_service() plist 示例。 +- node: 修复未定义值的 spawn() 选项逻辑。感谢 [@as0ler][] 报告并帮助追踪此问题! +- node: 将 aux 选项编组为 GVariant 时跳过 undefined。 +- node: 将对象编组为 GVariant 时跳过 undefined。 +- node: 处理编组为 GVariant 时的错误。 +- node: 修复 openService() plist 示例。 + +感谢 [@hsorbo][] 在上述所有方面进行有趣且富有成效的结对编程!🙌 + +注意:由于 CI 问题,此版本从未发布,已在 16.3.3 中解决。 + + +[@_saagarjha]: https://twitter.com/_saagarjha +[@as0ler]: https://twitter.com/as0ler +[@hsorbo]: https://twitter.com/hsorbo diff --git a/_i18n/cn/_posts/2024-06-04-frida-16-3-3-released.markdown b/_i18n/cn/_posts/2024-06-04-frida-16-3-3-released.markdown new file mode 100644 index 00000000..69c86eb8 --- /dev/null +++ b/_i18n/cn/_posts/2024-06-04-frida-16-3-3-released.markdown @@ -0,0 +1,10 @@ +--- +layout: news_item +title: 'Frida 16.3.3 发布' +date: 2024-06-04 14:14:54 +0200 +author: oleavr +version: 16.3.3 +categories: [release] +--- + +由于 CI 问题,上一版本没有发布。此版本中唯一的更改是所需的 CI 修复。(其中一个 FreeBSD 运行器镜像刚刚被 CI 提供商删除,因此我们不得不升级到一个较新的镜像。) diff --git a/_i18n/cn/_posts/2024-07-05-frida-16-4-0-released.markdown b/_i18n/cn/_posts/2024-07-05-frida-16-4-0-released.markdown new file mode 100644 index 00000000..32a9b8c0 --- /dev/null +++ b/_i18n/cn/_posts/2024-07-05-frida-16-4-0-released.markdown @@ -0,0 +1,52 @@ +--- +layout: news_item +title: 'Frida 16.4.0 发布' +date: 2024-07-05 15:32:58 +0200 +author: oleavr +version: 16.4.0 +categories: [release] +--- + +此版本包含一些令人兴奋的新事物。让我们直接潜入。 + +## CoreDevice + +正如 16.3.0 发行说明中提到的,[@hsorbo][] 和我正在致力于为 Linux 内核的 CDC-NCM 驱动程序提交补丁,以使其与 Apple 的专用网络接口兼容。这已经进入 [upstream][],并将成为 Linux 6.11 的一部分。 + +与此同时,对于那些在 Windows 上使用 Frida 的人,我们刚刚实现了一个 [minimal user-mode driver][],当 Frida 检测到内核没有提供驱动程序时,它现在会使用该驱动程序。我们利用 [lwIP][] 完全在用户空间中执行以太网和 IPv6。结果是 Frida 可以在 libusb 支持的任何平台上支持 CoreDevice。 + +## EOF + +还有很多其他令人兴奋的更改,所以一定要查看下面的变更日志。 + +享受吧! + +### 变更日志 + +- fruity: 重做以支持用户空间 CDC-NCM。 +- fruity: 添加对 iOS >= 18 上 dyld 重启的支持。 +- fruity: 在 iOS >= 18 上等待 ObjC 运行时初始化。 +- fruity: 修复没有 usbmux 连接可用时的 gadget 上传。 +- fruity: 改进 open_channel() 以支持 tcp:service-name。 +- fruity: 失败时重试 RSD 端口查找。 +- fruity: 恢复 HostChannelProvider 实现。 +- fruity: 如果 libSystem 已初始化,则跳过获取 dyld 符号。 +- fruity: 连接 MacOSCoreDeviceTransport 事件处理。 +- fruity: 修复 macOS CoreDevice 连接类型逻辑。 +- fruity: 将 `os.build` 和 `hardware` 添加到公开的系统参数。感谢 [@as0ler][]! +- server 和 gadget: 在 iOS 和 tvOS 上侦听 Apple 的 CoreDevice 隧道网络接口。 +- xpc-service: 修复带数组的 request() 处理。感谢 [@hsorbo][]! +- xpc-service: 支持类型注释请求参数。 +- python: 支持将元组解组为 GVariant。 +- python: 修复将 bool 解组为 GVariant。 +- node: 支持在编组为 GVariant 时进行类型注释。 +- node: 将 Node.js 要求提高到 `>=16 || 14 >=14.17`,以匹配 minimatch。 +- java: 修复 Android 上的 registerClass() 字段项排序。感谢 [@eybisi][]! + + +[@hsorbo]: https://twitter.com/hsorbo +[upstream]: https://github.com/torvalds/linux/commit/3ec8d7572a69d142d49f52b28ce8d84e5fef9131 +[minimal user-mode driver]: https://github.com/frida/frida-core/blob/31188db39a7c9ae24f640a34b3fdf701f4a93bb3/src/fruity/ncm.vala +[lwIP]: https://savannah.nongnu.org/projects/lwip/ +[@as0ler]: https://twitter.com/as0ler +[@eybisi]: https://github.com/eybisi diff --git a/_i18n/cn/_posts/2024-07-06-frida-16-4-1-released.markdown b/_i18n/cn/_posts/2024-07-06-frida-16-4-1-released.markdown new file mode 100644 index 00000000..9ba273e7 --- /dev/null +++ b/_i18n/cn/_posts/2024-07-06-frida-16-4-1-released.markdown @@ -0,0 +1,13 @@ +--- +layout: news_item +title: 'Frida 16.4.1 发布' +date: 2024-07-06 00:07:41 +0200 +author: oleavr +version: 16.4.1 +categories: [release] +--- + +快速的错误修复版本,旨在解决几个问题: + +- device: 修复 `id` 和 `name` 的属性获取器。 +- socket: 修复 negotiate_connection() 中的 `Host` 头逻辑。 diff --git a/_i18n/cn/_posts/2024-07-08-frida-16-4-2-released.markdown b/_i18n/cn/_posts/2024-07-08-frida-16-4-2-released.markdown new file mode 100644 index 00000000..91239669 --- /dev/null +++ b/_i18n/cn/_posts/2024-07-08-frida-16-4-2-released.markdown @@ -0,0 +1,13 @@ +--- +layout: news_item +title: 'Frida 16.4.2 发布' +date: 2024-07-08 14:27:19 +0200 +author: oleavr +version: 16.4.2 +categories: [release] +--- + +快速的错误修复版本,包含几个 Fruity 后端改进: + +- fruity: 处理 libusb 初始化失败。 +- fruity: 清理特定于操作系统的位。 diff --git a/_i18n/cn/_posts/2024-07-13-frida-16-4-3-released.markdown b/_i18n/cn/_posts/2024-07-13-frida-16-4-3-released.markdown new file mode 100644 index 00000000..3d7413f5 --- /dev/null +++ b/_i18n/cn/_posts/2024-07-13-frida-16-4-3-released.markdown @@ -0,0 +1,24 @@ +--- +layout: news_item +title: 'Frida 16.4.3 发布' +date: 2024-07-13 00:34:16 +0200 +author: oleavr +version: 16.4.3 +categories: [release] +--- + +此版本包含大量改进: + +- fruity: 添加对 Windows 和 Linux 上联网设备的支持。 +- ncm: 如果需要,切换 USB 设备配置。 +- network-stack: 修复 TcpConnection use-after-free。 +- fruity: 修复 LWIP.NetworkInterface 绑定。 +- fruity: 删除被遗忘的 .pcap 调试输出。 +- dtx: 修复 DTXConnection 与 DTXChannel 的拆卸。 +- buffer: 向 read_string() 添加内容验证。 +- buffer: 向 read_fixed_string() 添加内容验证。 +- java: 修复 Android >= 14 上的 Java.choose()。感谢 [@mbricchi][]! +- java: 处理 CMC GC 策略。感谢 [@mbricchi][]! + + +[@mbricchi]: https://github.com/mbricchi diff --git a/_i18n/cn/_posts/2024-07-16-frida-16-4-4-released.markdown b/_i18n/cn/_posts/2024-07-16-frida-16-4-4-released.markdown new file mode 100644 index 00000000..fe7f9aab --- /dev/null +++ b/_i18n/cn/_posts/2024-07-16-frida-16-4-4-released.markdown @@ -0,0 +1,26 @@ +--- +layout: news_item +title: 'Frida 16.4.4 发布' +date: 2024-07-16 15:25:08 +0200 +author: oleavr +version: 16.4.4 +categories: [release] +--- + +此版本包含大量改进: + +- darwin: 处理 macOS Sequoia 和 iOS 18 上的 dyld 重启。 +- darwin: 在 macOS Sequoia 和 iOS 18 上等待 ObjC 初始化,如果有的话利用 notifyObjCInit()。 +- fruity: 改进 CoreDevice 配对支持: + - 修复对多重配对关系的支持。 + - 保持内存中的对等体存储最新,因此新的配对关系不需要重启进程就能与网络上的配对服务匹配。 +- ncm: 在更改配置之前分离所有驱动程序。 +- ncm: 避免使用损坏的内核 NCM 驱动程序。 +- darwin: 修复模拟器上的 sysroot。感谢 [@CodeColorist][]! +- darwin-mapper: 本地解析共享缓存符号,尽可能避免解析器函数,从而避开我们现有的生成构造函数尝试将结果写入只读页面的问题。 +- gumjs: 修复应用程序线程上 recv().wait() 中的竞争。感谢 [@HexKitchen][]! +- python: 消除不稳定的引用计数 API 的使用,以便使用较新 Python 头文件构建的扩展仍然可以在较旧的 Python 运行时上工作。 + + +[@CodeColorist]: https://twitter.com/CodeColorist +[@HexKitchen]: https://github.com/HexKitchen diff --git a/_i18n/cn/_posts/2024-07-17-frida-16-4-5-released.markdown b/_i18n/cn/_posts/2024-07-17-frida-16-4-5-released.markdown new file mode 100644 index 00000000..8eae4292 --- /dev/null +++ b/_i18n/cn/_posts/2024-07-17-frida-16-4-5-released.markdown @@ -0,0 +1,17 @@ +--- +layout: news_item +title: 'Frida 16.4.5 发布' +date: 2024-07-17 22:43:23 +0200 +author: oleavr +version: 16.4.5 +categories: [release] +--- + +快速的错误修复版本,旨在解决几个问题: + +- xpc-client: 在 request() 中连接 cancellable,以支持在我们的 Fruity macOS CoreDevice 后端取消 remotepairingd 请求。 +- java: 正确处理 Android ART CMC GC 策略。感谢 [@mbricchi][]! +- java: 修复较新 ART APEX 上的 Java.choose()。感谢 [@mbricchi][]! + + +[@mbricchi]: https://github.com/mbricchi diff --git a/_i18n/cn/_posts/2024-07-22-frida-16-4-6-released.markdown b/_i18n/cn/_posts/2024-07-22-frida-16-4-6-released.markdown new file mode 100644 index 00000000..c9b673c2 --- /dev/null +++ b/_i18n/cn/_posts/2024-07-22-frida-16-4-6-released.markdown @@ -0,0 +1,22 @@ +--- +layout: news_item +title: 'Frida 16.4.6 发布' +date: 2024-07-22 21:47:49 +0200 +author: oleavr +version: 16.4.6 +categories: [release] +--- + +此版本包含大量改进: + +- fruity: 如果存在,使用来自 USB 传输的 UsbmuxDevice,以便我们可以访问环回接口,并获得更好的性能。 +- fruity: 处理非 macOS 上不支持隧道的设备。 +- fruity: 如果存在,从 USB 传输公开名称。如果是 UsbmuxTransport,它具有更具描述性的名称。 +- gumjs: 确保在构建 devkit 之前构建 Gum .a。感谢 [@Hexploitable][]! +- gumjs: 生成更简单的枚举值查找代码。 +- spinlock: 合并为单个实现。 +- java: 修复 Android >= 15 的 art::Thread::DecodeJObject。感谢 [@esauvisky][]! + + +[@Hexploitable]: https://twitter.com/Hexploitable +[@esauvisky]: https://github.com/esauvisky diff --git a/_i18n/cn/_posts/2024-07-23-frida-16-4-7-released.markdown b/_i18n/cn/_posts/2024-07-23-frida-16-4-7-released.markdown new file mode 100644 index 00000000..3b1a5fea --- /dev/null +++ b/_i18n/cn/_posts/2024-07-23-frida-16-4-7-released.markdown @@ -0,0 +1,15 @@ +--- +layout: news_item +title: 'Frida 16.4.7 发布' +date: 2024-07-23 10:15:09 +0200 +author: oleavr +version: 16.4.7 +categories: [release] +--- + +快速的错误修复版本,旨在解决 Windows 上的崩溃: + +- fruity: 修复在 Windows 上枚举网络接口时的崩溃。有些接口没有单播地址,需要被忽略。感谢 [@xiofee][]! + + +[@xiofee]: https://github.com/xiofee diff --git a/_i18n/cn/_posts/2024-08-02-frida-16-4-8-released.markdown b/_i18n/cn/_posts/2024-08-02-frida-16-4-8-released.markdown new file mode 100644 index 00000000..fe93c926 --- /dev/null +++ b/_i18n/cn/_posts/2024-08-02-frida-16-4-8-released.markdown @@ -0,0 +1,18 @@ +--- +layout: news_item +title: 'Frida 16.4.8 发布' +date: 2024-08-02 14:05:27 +0200 +author: oleavr +version: 16.4.8 +categories: [release] +--- + +快速的错误修复版本,旨在进一步改进我们的 Fruity 后端: + +- lockdown-client: 冒泡 CONNECTION_CLOSED 错误。感谢 [@hsorbo][]! +- fruity: 使 find_usbmux_device() 不抛出异常。感谢 [@hsorbo][]! +- fruity: 防止 lockdown open -> close 循环。 +- fruity: 修复已关闭 lockdown 客户端的失效。感谢 [@hsorbo][]! + + +[@hsorbo]: https://twitter.com/hsorbo diff --git a/_i18n/cn/_posts/2024-08-20-frida-16-4-9-released.markdown b/_i18n/cn/_posts/2024-08-20-frida-16-4-9-released.markdown new file mode 100644 index 00000000..f5ec92a1 --- /dev/null +++ b/_i18n/cn/_posts/2024-08-20-frida-16-4-9-released.markdown @@ -0,0 +1,19 @@ +--- +layout: news_item +title: 'Frida 16.4.9 发布' +date: 2024-08-20 21:50:56 +0200 +author: oleavr +version: 16.4.9 +categories: [release] +--- + +快速的错误修复版本,旨在解决一些问题: + +- tunnel-interface-observer: 修复 i/tvOS 上 SCDynamicStoreCopyKeyList() 失败时的 start() 崩溃。感谢 [@mrmacete][]! +- darwin-mapper: 在构造函数之前初始化 TLV。感谢 [@jiska][]! +- darwin-mapper: 修复 arm64 的 TLV 初始化运行时代码。感谢 [@jiska][]! +- arm64-writer: 修复 UBFM 和 LS{L,R} 的编码。感谢 [@jiska][]! + + +[@mrmacete]: https://twitter.com/bezjaje +[@jiska]: https://chaos.social/@jiska diff --git a/_i18n/cn/_posts/2024-08-22-frida-16-4-10-released.markdown b/_i18n/cn/_posts/2024-08-22-frida-16-4-10-released.markdown new file mode 100644 index 00000000..4a3988d7 --- /dev/null +++ b/_i18n/cn/_posts/2024-08-22-frida-16-4-10-released.markdown @@ -0,0 +1,18 @@ +--- +layout: news_item +title: 'Frida 16.4.10 发布' +date: 2024-08-22 15:40:59 +0200 +author: oleavr +version: 16.4.10 +categories: [release] +--- + +快速的错误修复版本,旨在进一步改进我们的 Fruity 后端,[@hsorbo][] 和我倒满咖啡,敲定了以下修复: + +- tunnel-connection: 连接缺失的流关闭逻辑。 +- tunnel-connection: 确保 JSON 请求最终在自己的 UDP 数据包中。打开隧道时,如果数据报和流数据最终在同一个 UDP 数据包中,似乎会出现问题。我们使虚拟数据报更大以避免这种情况。 +- tunnel-connection: 始终避免在连接消失后写入,这将导致崩溃。 +- network-stack: 从 lwIP 线程处理 perform_on_lwip_thread(),而不是死锁。 + + +[@hsorbo]: https://twitter.com/hsorbo diff --git a/_i18n/cn/_posts/2024-09-06-frida-16-5-0-released.markdown b/_i18n/cn/_posts/2024-09-06-frida-16-5-0-released.markdown new file mode 100644 index 00000000..80d40ff2 --- /dev/null +++ b/_i18n/cn/_posts/2024-09-06-frida-16-5-0-released.markdown @@ -0,0 +1,204 @@ +--- +layout: news_item +title: 'Frida 16.5.0 发布' +date: 2024-09-06 21:57:17 +0200 +author: oleavr +version: 16.5.0 +categories: [release] +--- + +你们中的一些人可能遇到过这样的情况:内存中的一段数据看起来很有趣,你想找到负责它的代码。你可能尝试过 Frida 的 MemoryAccessMonitor API,但发现页面粒度很难处理。也就是说,你可能不得不收集许多样本,直到你幸运地捕获到访问该页面上特定字节的代码。这在具有 4K 页面的系统上可能已经够难了,在具有 16K 页面的现代 Apple 系统上甚至更糟。 + +为了解决这个问题,[@hsorbo][] 和我倒满咖啡开始工作,实现了对硬件断点和观察点的支持。长话短说,Process.enumerateThreads() 返回的线程对象现在具有 setHardwareBreakpoint()、setHardwareWatchpoint() 以及稍后取消设置它们的相应方法。然后将这些与 Process.setExceptionHandler() 结合使用,在其中调用取消设置方法并返回 true 以表示异常已处理,应恢复执行。 + +## 演示时间 + +让我们试用一下这些新 API。作为目标,我们将选择 id Software 全新的 2024 年重新发布的 DOOM + DOOM II。 + +![DOOM](/img/doom-e1m1.jpg "DOOM E1M1") + +我们可能想做的第一件事是弄清楚子弹数量存储在内存中的位置。让我们编写一个微型代理来帮助我们实现这一目标: + +{% highlight js %} +let matches = []; + +function scan(pattern) { + const locations = new Set(); + for (const r of Process.enumerateMallocRanges()) { + for (const match of Memory.scanSync(r.base, r.size, pattern)) { + locations.add(match.address.toString()); + } + } + matches = Array.from(locations).map(ptr); + console.log('Found', matches.length, 'matches'); +} + +function reduce(val) { + matches = matches.filter(location => location.readU32() === val); + console.log('Filtered down to:'); + console.log(JSON.stringify(matches)); +} + +function patternFromU32(val) { + return new MatchPattern(ptr(val).toMatchPattern().substr(0, 11)); +} +{% endhighlight %} + +并将其加载到游戏中: + +{% highlight sh %} +$ frida -n doom.exe -l demo.js +… +[Local::doom.exe ]-> +{% endhighlight %} + +我们知道目前有 50 发子弹,所以让我们找到所有包含值 50 的堆分配,编码为本机 uint32: + +{% highlight sh %} +[Local::doom.exe ]-> scan(patternFromU32(50)) +Found 6947 matches +{% endhighlight %} + +这相当多。让我们通过发射一颗子弹来缩小范围,并检查这些位置中哪些现在包含值 49: + +{% highlight sh %} +[Local::doom.exe ]-> reduce(49) +Filtered down to: +["0x1fbf5191884"] +{% endhighlight %} + +宾果!既然我们知道子弹数量存储在内存中的位置,我们的下一步是找到发射子弹时更新子弹数量的代码。让我们向代理添加另一个辅助函数: + +{% highlight js %} +function installWatchpoint(address, size, conditions) { + const thread = Process.enumerateThreads()[0]; + + Process.setExceptionHandler(e => { + console.log(`\n=== Handler got ${e.type} exception at ${e.context.pc}`); + + if (Process.getCurrentThreadId() === thread.id && + ['breakpoint', 'single-step'].includes(e.type)) { + thread.unsetHardwareWatchpoint(0); + console.log('\tDisabled hardware watchpoint'); + return true; + } + + console.log('\tPassing to application'); + return false; + }); + + thread.setHardwareWatchpoint(0, address, size, conditions); + + console.log('Ready'); +} +{% endhighlight %} + +并调用它: + +{% highlight sh %} +[Local::doom.exe ]-> installWatchpoint(ptr('0x1fbf5191884'), 4, 'w') +Ready +{% endhighlight %} + +接下来我们将切换回游戏并再发射一颗子弹: + +{% highlight sh %} +[Local::doom.exe ]-> +=== Handler got system exception at 0x7ffc2bc2fabc + Passing to application + +=== Handler got single-step exception at 0x7ff6f0a21010 + Disabled hardware watchpoint +{% endhighlight %} + +耶,看起来很有希望。让我们对该地址进行符号化: + +{% highlight sh %} +[Local::doom.exe ]-> ammoCode = ptr('0x7ff6f0a21010') +"0x7ff6f0a21010" +[Local::doom.exe ]-> ammoModule = Process.getModuleByAddress(ammoCode) +{ + "base": "0x7ff6f0730000", + "name": "DOOM.exe", + "path": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Ultimate Doom\\rerelease\\DOOM.exe", + "size": 15495168 +} +[Local::doom.exe ]-> offset = ammoCode.sub(ammoModule.base) +"0x2f1010" +{% endhighlight %} + +让我们使用 r2 仔细看看: + +![DOOM](/img/doom-r2.png "DOOM static analysis") + +我们可以看到,我们在异常处理程序中观察到的程序计数器位于触发我们观察点的 `sub` 之后的指令上。 + +所以从这里我们可以设置一个内联 hook,只要发射子弹就会触发: + +{% highlight js %} +Interceptor.attach(Module.getBaseAddress('doom.exe').add(0x2f1010), function () { + const ammoLeft = this.context.rax.add(4).readU32(); + console.log(`Shots fired! Ammo left: ${ammoLeft}`); +}); +{% endhighlight %} + +{% highlight sh %} +[Local::doom.exe ]-> Shots fired! Ammo left: 42 +Shots fired! Ammo left: 41 +Shots fired! Ammo left: 40 +Shots fired! Ammo left: 39 +Shots fired! Ammo left: 38 +{% endhighlight %} + +我们可以同样轻松地为自己制作一个无限弹药作弊: + +{% highlight js %} +Interceptor.attach(Module.getBaseAddress('doom.exe').add(0x2f100d), function () { + this.context.rbx = ptr(0); + console.log(`Shots fired! Pretending no ammo was actually used`); +}); +{% endhighlight %} + +{% highlight sh %} +[Local::doom.exe ]-> Shots fired! Pretending no ammo was actually used +Shots fired! Pretending no ammo was actually used +Shots fired! Pretending no ammo was actually used +Shots fired! Pretending no ammo was actually used +Shots fired! Pretending no ammo was actually used +{% endhighlight %} + +看妈,无限弹药! + +请注意,我们也可以通过使用 Memory.patchCode() 将 `sub` 替换为 3 字节的 `nop` 来实现这一点,X86Writer 可以通过 putNopPadding(3) 为我们做到这一点。Interceptor hook 的优点是当我们的脚本卸载时会自动回滚,并且很容易执行任意代码。 + +## Windows on ARM + +此版本的另一个亮点是我们现在支持 Windows on ARM。这意味着 arm64 版本的 Frida 可以注入本机 arm64 进程,以及模拟的 x86_64 和 x86 进程。 + +但是我们还没有提供二进制文件,因为我们在等待 GitHub 向 OSS 项目提供 arm64 运行器,目前仅限于他们的团队和企业云客户。虽然从 x86_64 构建机器交叉编译在技术上是可行的,但我们决定推迟这一点,因为我们很快遇到了 Meson 的 MSVC 支持问题。 + +## EOF + +还有大量其他令人兴奋的更改,所以一定要查看下面的变更日志。 + +享受吧! + +## 变更日志 + +- thread: 支持硬件断点和观察点。 +- fruity: 修复 perform_on_lwip_thread() 中的死锁。 +- windows: 添加对 arm64 的支持。 +- windows: 将 Exceptor 迁移到 Microsoft 的 VEH API。 +- linux: 处理分离时进程消失的情况。感谢 [@ajwerner][]! +- linux: 修复 MIPS 上的 clone() 包装器。 +- java: 处理未导出的 Android GC 循环处理程序。感谢 [@thinhbuzz][]! +- java: 添加对 Windows 上 OpenJDK 17 的初步支持。感谢 [@FrankSpierings][]! +- meson: 将 frida-netif 添加到公共 frida-core,以便 frida-core devkits 包含所有需要的符号。 +- node: 添加 Cancellable.withTimeout() 便利工厂函数。感谢 [@hsorbo][]! +- node: 添加 Cancellable.combine() 便利方法。感谢 [@hsorbo][]! + + +[@hsorbo]: https://twitter.com/hsorbo +[@ajwerner]: https://github.com/ajwerner +[@thinhbuzz]: https://github.com/thinhbuzz +[@FrankSpierings]: https://github.com/FrankSpierings diff --git a/_i18n/cn/_posts/2024-09-06-frida-16-5-1.markdown b/_i18n/cn/_posts/2024-09-06-frida-16-5-1.markdown new file mode 100644 index 00000000..beaec72d --- /dev/null +++ b/_i18n/cn/_posts/2024-09-06-frida-16-5-1.markdown @@ -0,0 +1,10 @@ +--- +layout: news_item +title: 'Frida 16.5.1 发布' +date: 2024-09-06 23:00:05 +0200 +author: oleavr +version: 16.5.1 +categories: [release] +--- + +上一版本的二进制文件没有发布,因为 watchOS 上出现了构建回归,此版本已迅速修复。 diff --git a/_i18n/cn/_posts/2024-09-19-frida-16-5-2.markdown b/_i18n/cn/_posts/2024-09-19-frida-16-5-2.markdown new file mode 100644 index 00000000..87ac4529 --- /dev/null +++ b/_i18n/cn/_posts/2024-09-19-frida-16-5-2.markdown @@ -0,0 +1,21 @@ +--- +layout: news_item +title: 'Frida 16.5.2 发布' +date: 2024-09-19 00:01:01 +0200 +author: oleavr +version: 16.5.2 +categories: [release] +--- + +快速的错误修复版本,旨在进一步改进我们的 Fruity 后端,[@hsorbo][] 和我倒满咖啡,敲定了以下修复和调整: + +- fruity: 当隧道设置失败时重用 NCM 对等体。 +- fruity: 处理未与任何人配对的 iDevice。 +- fruity: 处理 USB TunnelConnection 掉线。 +- fruity: 修复不可靠的模式切换激活。 +- fruity: 修复使用我们的 NCM 驱动程序时的拆卸。 +- fruity: 修复非 macOS 的 find_tunnel() 中的错误编组。 +- fruity: 宣传我们的 RemoteXPC 协议支持。 + + +[@hsorbo]: https://twitter.com/hsorbo diff --git a/_i18n/cn/_posts/2024-10-10-frida-16-5-3.markdown b/_i18n/cn/_posts/2024-10-10-frida-16-5-3.markdown new file mode 100644 index 00000000..508dd27d --- /dev/null +++ b/_i18n/cn/_posts/2024-10-10-frida-16-5-3.markdown @@ -0,0 +1,30 @@ +--- +layout: news_item +title: 'Frida 16.5.3 发布' +date: 2024-10-10 20:18:36 +0200 +author: oleavr +version: 16.5.3 +categories: [release] +--- + +很高兴为您带来另一个错误修复版本,以进一步改进我们的 Fruity 后端、iOS 稳定性,并修复使用正则表达式进行的内存扫描。 + +未具体归属的更改是由 [@hsorbo][] 和我在一系列有趣的结对编程会议中编写的。 + +长话短说: + +- memory: 使内存扫描正则表达式模式原始化,以便搜索在非有效 UTF-8 的二进制区域中是可靠的。感谢 [@mrmacete][]! +- web-service: 关闭已删除的动态接口的连接,以避免它们一直存在直到我们耗尽文件描述符。 +- network-stack: 处理 TcpConnection 的突然处置,我们会释放 TCP PCB 但未能通知任何活动的 TcpIOSource 实例和阻塞的 TcpInputStream.read() 调用。 +- network-stack: 修复对等关闭时的竞争性 TCP 数据丢失,其中关闭导致我们释放 PCB,并且 recv() 逻辑会因为 PCB 消失而提前返回。此时 RX 缓冲区中可能仍有数据,但应用程序看不到它。 +- network-stack: 修复 TcpInputStream.read() 中的竞争,其中 notify::pending-io 信号在调用 is_readable() 之后但在连接我们的信号处理程序之前触发。 +- fruity: 使用 USB 产品字符串作为传输名称。 +- fruity: 修复 iOS < 16 上的 USB 模式解析,其中模式是一个 3 字节的 blob。 +- fruity: 冒泡 USB 权限错误。 +- fruity: 在 iOS 隧道服务版本早于 17 时退出,而不是崩溃。 +- control-service: 修复 CoreDevice 系统上的可靠性,其中在所有接口上侦听的单个传输代理可能会在尝试从隧道内部与其通信时导致早期 TCP RST。确切原因尚不清楚,但我们已确认每个动态接口/隧道拥有一个代理确实可以解决此问题。我们还观察到,在所有接口上侦听但仅限于 IPv6 也可以避免此问题。 +- meson: 修复将 GLib 作为子项目的 i/tvOS 编译。 +- meson: 在 FreeBSD 上禁用 Fruity 及其朋友,因为我们现在依赖于仅存在于我们的 libusb 中的 libusb API。如果有人对 FreeBSD 上的这些后端感兴趣,修复这个问题应该不难——非常欢迎 PR。 + + +[@mrmacete]: https://twitter.com/bezjaje diff --git a/_i18n/cn/_posts/2024-10-10-frida-16-5-4.markdown b/_i18n/cn/_posts/2024-10-10-frida-16-5-4.markdown new file mode 100644 index 00000000..ee4df057 --- /dev/null +++ b/_i18n/cn/_posts/2024-10-10-frida-16-5-4.markdown @@ -0,0 +1,10 @@ +--- +layout: news_item +title: 'Frida 16.5.4 发布' +date: 2024-10-10 22:08:02 +0200 +author: oleavr +version: 16.5.4 +categories: [release] +--- + +上一版本的二进制文件没有发布,因为 frida-node 的 NAN 依赖项被一个本不该升级它的脚本升级了,而且最新的代码破坏了 Electron 支持。此版本将其回滚,并同时将 frida.Compiler 的 @types/frida-gum 升级到 18.7.1。 diff --git a/_i18n/cn/_posts/2024-10-11-frida-16-5-5.markdown b/_i18n/cn/_posts/2024-10-11-frida-16-5-5.markdown new file mode 100644 index 00000000..b41723fb --- /dev/null +++ b/_i18n/cn/_posts/2024-10-11-frida-16-5-5.markdown @@ -0,0 +1,13 @@ +--- +layout: news_item +title: 'Frida 16.5.5 发布' +date: 2024-10-11 11:18:40 +0200 +author: oleavr +version: 16.5.5 +categories: [release] +--- + +事实证明,一个严重的稳定性回归进入了 Frida 16.5.3,其中 frida-server 会在非 CoreDevice 隧道发起的传入连接上崩溃。感谢 [@mrmacete][] 在有问题的版本发布仅几小时后就调查并修复了此问题。🎉 享受吧! + + +[@mrmacete]: https://twitter.com/bezjaje diff --git a/_i18n/cn/_posts/2024-10-14-frida-16-5-6.markdown b/_i18n/cn/_posts/2024-10-14-frida-16-5-6.markdown new file mode 100644 index 00000000..1de5b984 --- /dev/null +++ b/_i18n/cn/_posts/2024-10-14-frida-16-5-6.markdown @@ -0,0 +1,17 @@ +--- +layout: news_item +title: 'Frida 16.5.6 发布' +date: 2024-10-14 15:32:29 +0200 +author: oleavr +version: 16.5.6 +categories: [release] +--- + +快速的错误修复版本,旨在进一步改进我们的 Fruity 后端,[@hsorbo][] 和我倒满咖啡,敲定了以下修复: + +- fruity: 修复 TcpConnection 中的 use-after-free。错误回调可能会在 PCB 已经被释放的时候被调用。这意味着我们清除其用户数据会导致 use-after-free,其中 NULL 指针被写入未知区域。 +- fruity: 修复 DTXArgumentList.parse() GValue 初始化,我们在遇到对象时使用了错误的设置器。这被 GLib 的运行时检查捕获了,但由于我们通常在没有它们的情况下构建,因此未被注意到。 +- payload: 修复 AddressSanitizer 构建回归。 + + +[@hsorbo]: https://twitter.com/hsorbo diff --git a/_i18n/cn/_posts/2024-11-14-frida-16-5-7.markdown b/_i18n/cn/_posts/2024-11-14-frida-16-5-7.markdown new file mode 100644 index 00000000..3d814855 --- /dev/null +++ b/_i18n/cn/_posts/2024-11-14-frida-16-5-7.markdown @@ -0,0 +1,55 @@ +--- +layout: news_item +title: 'Frida 16.5.7 发布' +date: 2024-11-14 00:30:50 +0100 +author: oleavr +version: 16.5.7 +categories: [release] +--- + +令人兴奋的新版本,包含了跨平台的改进和新功能。以下是新内容: + +#### Fruity 后端改进 + +[@hsorbo][] 和我一直在努力增强我们的 Fruity 后端,我们很高兴分享以下改进: + +- **添加了对 TCP 隧道协议的支持** 并将其设为默认值以匹配 Apple 的新行为。可以使用 `FRIDA_FRUITY_TUNNEL_PROTOCOL` 环境变量恢复为 QUIC。 +- **修复了旧 OS 版本隧道逻辑中的边缘情况**,即使在 `usbmuxd` 不可用的情况下也能确保可靠运行。 +- **优雅地关闭 `TunnelConnection`** 以提高稳定性。 +- **修复了拆卸期间启动 USB 操作时可能发生的挂起**,防止传输卡住。 +- **修复了 `QuicTunnelConnection` 拆卸逻辑**,正确处理关闭操作期间的错误。 +- **放宽了 NCM 接口存在检查**,只需要一个可操作的网络接口。 +- **调整为始终首先尝试 USB 传输**,因为网络传输可能较慢或不可用。 +- **改进了跳过 jailed 回退时的超时情况处理**。 +- **现在期望联网设备在连接到配对服务时快速响应**,改善设备进入睡眠状态时的用户体验。 +- **修复了现代设备的 `CoreDevice` UDID 逻辑**。 + +#### Android + +- **Gadget 现在支持在 `extractNativeLibs` 设置为 `false` 时从 APK 加载资产**,提高了与现代 Android 应用程序的兼容性(感谢 [@gergesh][])。 +- **恢复了注入器对共享 `libc` 范围的处理**,确保当目标进程的最低 `libc.so` 范围是共享映射时的正确行为(感谢 [@lx866][])。 + +#### Linux + +- **改进了注入器与 MUSL 的兼容性**,处理加载器字符串中的差异(感谢 [@luckycat889][])。 +- **添加了对构建助手时覆盖配置的支持**,允许在构建中具有更大的灵活性(感谢 [@luckycat889][])。 +- **为注入器的远程调用分配了堆栈**,提高了与使用小堆栈的程序(如 Go 应用程序)的兼容性(感谢 [@ajwerner][])。 +- **添加了重建助手二进制文件的 CI**,以确保源代码和签入的二进制文件之间的一致性(感谢 [@ajwerner][])。 +- **当 `$TMPDIR` 为 `noexec` 时选择替代临时目录** + +#### 跨平台 + +- **在构建系统中添加了对非 UTF-8 语言环境的支持**,确保在具有各种语言环境设置的系统上有更好的兼容性(感谢 [@JunGe-Y][])。 +- **在构建系统中添加了对 PowerPC 架构的支持**。 +- **在 `frida-inject` 和 RpcClient 内部添加了对二进制数据处理的支持**。 + +祝黑客愉快! + +--- + +[@hsorbo]: https://twitter.com/hsorbo +[@gergesh]: https://github.com/gergesh +[@lx866]: https://github.com/lx866 +[@luckycat889]: https://github.com/luckycat889 +[@ajwerner]: https://github.com/ajwerner +[@JunGe-Y]: https://github.com/JunGe-Y diff --git a/_i18n/cn/_posts/2024-12-09-frida-16-5-8-released.markdown b/_i18n/cn/_posts/2024-12-09-frida-16-5-8-released.markdown new file mode 100644 index 00000000..3c8fdecb --- /dev/null +++ b/_i18n/cn/_posts/2024-12-09-frida-16-5-8-released.markdown @@ -0,0 +1,37 @@ +--- +layout: news_item +title: 'Frida 16.5.8 发布' +date: 2024-12-09 00:32:29 +0100 +author: oleavr +version: 16.5.8 +categories: [release] +--- + +令人兴奋的新版本,包含了各个组件的性能增强和错误修复,特别是在我们的 Fruity 后端。[@hsorbo][] 和我合作为您带来了以下改进: + +- **fruity:** 通过多重传输提升 NCM 性能,提高数据传输效率。 +- **fruity:** 改进用户空间 NCM 驱动程序以执行批处理,减少突发情况下的数据包丢失。 +- **fruity:** 启用 lwIP TCP 时间戳和 SACK 以与 Linux IP 堆栈默认值保持一致,增强网络性能。 +- **fruity:** 将 lwIP TCP 最大段大小 (MSS) 提高到 4036,以获得更好的 TCP 隧道性能。 +- **fruity:** 考虑 `frida-server` `bind()` 延迟以提高连接建立的可靠性。 +- **fruity:** 修复拆卸期间 USB 操作创建时的崩溃。 +- **fruity:** 通过在内核 NCM 可用时避免不必要的 USB 访问,改进非 macOS 系统上的 USB 设备处理。 +- **fruity:** 通过确保即使运行冲突的服务也能正确建立连接,修复直接通道的可靠性。感谢 [@mrmacete][] 帮助追踪此问题。 +- **fruity:** 改进 `TcpTunnelConnection` 拆卸,以确保在远程端关闭连接时进行适当的清理。 + +- **api:** 生成适当的 GObject Introspection Repository (GIR),包括必要的类型并省略内部类型。 +- **api:** 避免在 API 中公开内部类型。 +- **api:** 省略涉及 `HostSession` 的 API。 + +- **build:** 修改输出逻辑以避免对输出文件的冗余写入,从而加快增量构建速度。 +- **build:** 利用 Meson 对多个输出的 `custom_target()` 支持,避免多次解析 API。 +- **compat:** 创建相对子项目符号链接,以便可以在不破坏构建的情况下移动源代码树。 +- **compat:** 修复子项目 `compat.symlink_to()` 中的错误处理。 + +- **windows:** 修复不存在 PID 的 `cpu_type_from_pid()`。 +- **windows:** 在 Windows 11+ 上使用 `GetProcessInformation()` 以确保证确使用 `ProcessMachineTypeInfo`。 + +一如既往,非常感谢 [@hsorbo][] 和 [@mrmacete][] 为使此版本成为可能而做出的宝贵贡献。 + +[@hsorbo]: https://twitter.com/hsorbo +[@mrmacete]: https://twitter.com/mrmacete diff --git a/_i18n/cn/_posts/2024-12-09-frida-16-5-9-released.markdown b/_i18n/cn/_posts/2024-12-09-frida-16-5-9-released.markdown new file mode 100644 index 00000000..75c549ae --- /dev/null +++ b/_i18n/cn/_posts/2024-12-09-frida-16-5-9-released.markdown @@ -0,0 +1,16 @@ +--- +layout: news_item +title: 'Frida 16.5.9 发布' +date: 2024-12-09 00:52:39 +0100 +author: oleavr +version: 16.5.9 +categories: [release] +--- + +哎呀,软件很难!这是另一个快速发布,旨在解决我们今天早些时候遗漏的一个问题。 + +我们修复了 Meson 构建脚本中的一个问题,即在 frida-core 的最新更改之后,modulemap 依赖项未正确指定。具体来说,`core_public_h` 现在是一个自定义目标索引,因此我们不能再直接使用它了。相反,我们现在依赖于它的父级 `core_api`。 + +特别感谢 [@hsorbo][] 共同编写此修复程序。 + +[@hsorbo]: https://twitter.com/hsorbo diff --git a/_i18n/cn/_posts/2025-01-09-frida-16-6-0-released.markdown b/_i18n/cn/_posts/2025-01-09-frida-16-6-0-released.markdown new file mode 100644 index 00000000..e11d7813 --- /dev/null +++ b/_i18n/cn/_posts/2025-01-09-frida-16-6-0-released.markdown @@ -0,0 +1,34 @@ +--- +layout: news_item +title: 'Frida 16.6.0 发布' +date: 2025-01-09 13:18:51 +0100 +author: oleavr +version: 16.6.0 +categories: [release] +--- + +很高兴宣布 Frida 16.6.0,其特点是对模块符号处理的重大改进、性能增强和错误修复。 + +以下是亮点: + +- **android**: 提高 ART 兼容性: + - 既然 Frida 支持解析 `.gnu_debugdata`,那么当导出丢失时查找符号。 + - 处理 runFlip 签名的更改 (感谢 [@matbrik][])。 +- **android**: 修复 enumerateLoadedClasses() 中的溢出 (感谢 [@123edi10][])。增量创建和清理全局引用,而不是在开始和结束时一次性处理所有引用。 +- **android**: 在 registerClass() 中支持静态方法 (感谢 [@5andr0][])。 +- **java**: 修复 32 位系统上由 JVMTI 驱动的 Java.choose()。 +- **module**: 将 Module API 转换为实例方法,以便可以有效地执行多个查询。以前这仅在 JavaScript (GumJS) 级别建模,其中此类 JS 对象将模块的路径作为字符串,传递给每个查询,例如 `enumerateExports()`。底层 C API 现在也以相同的方式建模。 +- **gumjs**: 添加 `findSymbolByName()` 和 `getSymbolByName()` 方法。提供按名称直接、本机查找符号,而不是枚举所有符号并在 JavaScript 中过滤它们。 +- **module**: 优化 `find_symbol_by_name()` 回退。当 Module 实现缺乏优化的符号查找方法时,构建排序索引并对其进行二进制搜索。 +- **elf-module**: 如果未找到符号,则使用 MiniDebugInfo。当 `enumerate_symbols()` 遇到内存中没有符号的 ELF 时,实例化离线 `ElfModule` 实例并解析 `.gnu_debugdata` 部分。解压缩嵌入的 ELF 并重用其符号作为回退。 +- **ncm**: 通过将每次传输的数据报限制上限为 16 来提高我们的用户空间 USB CDC-NCM 驱动程序的性能 (感谢结对编程,[@hsorbo][]!)。 +- 移植到新的 `Gum.Module` API。过渡到实例方法以允许有效地执行多个查询。 +- 放弃对在没有 GObject 的情况下运行的支持。占用的节省微乎其微,并且不能证明增加的复杂性和降低的代码可读性是合理的。 +- **gumjs**: 修复 V8 `NativeCallback` use-after-free (非 Interceptor),其中 `CpuContext` 的作用域太窄。 + +一如既往,祝黑客愉快! + +[@matbrik]: https://github.com/matbrik +[@123edi10]: https://github.com/123edi10 +[@5andr0]: https://github.com/5andr0 +[@hsorbo]: https://twitter.com/hsorbo diff --git a/_i18n/cn/_posts/2025-01-10-frida-16-6-1-released.markdown b/_i18n/cn/_posts/2025-01-10-frida-16-6-1-released.markdown new file mode 100644 index 00000000..1d9da072 --- /dev/null +++ b/_i18n/cn/_posts/2025-01-10-frida-16-6-1-released.markdown @@ -0,0 +1,20 @@ +--- +layout: news_item +title: 'Frida 16.6.1 发布' +date: 2025-01-10 02:04:19 +0100 +author: oleavr +version: 16.6.1 +categories: [release] +--- + +一个包含一些重要修复和改进的新版本: + +- **gumjs**: 在取消引用模块时放弃 JS 锁。以避免在 `dispose()` 释放缓存句柄的情况下发生死锁。这种操作通常需要获取运行时链接器锁。另一个线程可能在等待 JS 锁时已经持有该锁。发生这种情况的一个常见场景是代理向运行时链接器注册一个回调,每当加载或卸载模块时都会调用该回调。 + + 感谢 [@mrmacete][] 的报告。 + +- **agent**: 在版本脚本中排除 OS/arch 符号。较新的工具链,例如 FreeBSD 14.2 上的默认工具链,不喜欢引用不存在的符号。我们不再列出仅为 Android 构建定义的 `JNI_OnLoad`,而是为 Android 使用单独的版本脚本。 + +- **ci**: 将 CI 移至 FreeBSD 14.2,从已停产的 14.0 升级。我们的 FreeBSD CI 在过去几周的某个时候坏了,直到导致上一个版本无法发布才被注意到。哎呀! + +[@mrmacete]: https://github.com/mrmacete diff --git a/_i18n/cn/_posts/2025-01-13-frida-16-6-2-released.markdown b/_i18n/cn/_posts/2025-01-13-frida-16-6-2-released.markdown new file mode 100644 index 00000000..9806f63d --- /dev/null +++ b/_i18n/cn/_posts/2025-01-13-frida-16-6-2-released.markdown @@ -0,0 +1,16 @@ +--- +layout: news_item +title: 'Frida 16.6.2 发布' +date: 2025-01-13 22:20:51 +0100 +author: oleavr +version: 16.6.2 +categories: [release] +--- + +另一轮改进和修复,以增强 Frida 的稳定性和性能,感谢 [@mrmacete][] 的宝贵反馈。以下是此版本的新内容: + +- **gumjs**: 通过使用空闲源推迟 unref 来修复 `Module` 终结器中的崩溃。这避免了由我们的 QuickJS 挂起/恢复补丁不支持从终结器使用引起的问题,并且还避免了在大容量模块销毁期间挂起/恢复 JS 执行的开销。更好的长期解决方案将涉及引入 `ModuleObserver` 来管理 `Module` 生命周期并在添加或删除模块时发出信号。 + +- **module**: 通过对所有 `Module` 对象使用单个锁来加速 `NativeModule` 生命周期。此更改提高了性能,一旦 GLib 静态分配清理补丁增强为使用更合适的互斥锁跟踪数据结构,将重新审视此更改。 + +[@mrmacete]: https://github.com/mrmacete diff --git a/_i18n/cn/_posts/2025-01-14-frida-16-6-3-released.markdown b/_i18n/cn/_posts/2025-01-14-frida-16-6-3-released.markdown new file mode 100644 index 00000000..8ec6cfb7 --- /dev/null +++ b/_i18n/cn/_posts/2025-01-14-frida-16-6-3-released.markdown @@ -0,0 +1,12 @@ +--- +layout: news_item +title: 'Frida 16.6.3 发布' +date: 2025-01-14 19:35:41 +0100 +author: oleavr +version: 16.6.3 +categories: [release] +--- + +此版本的主要变化是恢复了我们的 Windows 注入器,它被最近的 Gum.Module 重构破坏了。除此之外,我们还提高了跨平台低级 GLib 原语的性能,特别是在我们实现静态分配清理的补丁中。这是必要的,因为 Frida 注入的有效载荷的寿命可能比它注入的进程更短。 + +享受吧! diff --git a/_i18n/cn/_posts/2025-01-17-frida-16-6-4-released.markdown b/_i18n/cn/_posts/2025-01-17-frida-16-6-4-released.markdown new file mode 100644 index 00000000..9083795a --- /dev/null +++ b/_i18n/cn/_posts/2025-01-17-frida-16-6-4-released.markdown @@ -0,0 +1,17 @@ +--- +layout: news_item +title: 'Frida 16.6.4 发布' +date: 2025-01-17 16:49:19 +0100 +author: oleavr +version: 16.6.4 +categories: [release] +--- + +此版本带来了重要的修复和改进: + +- **objc**: 处理扩展块类型编码 (感谢 [@mrmacete][])。 +- **module**: 恢复了之前对 `NativeModule` 生命周期的优化,因为我们 GLib 补丁中的潜在性能问题已得到解决。 +- **darwin**: 修复了当提供别名时 `Module.load()` 可能失败的问题。在 macOS >= 13 上,我们现在使用 `_dyld_get_dlopen_image_header()` 按地址解析模块。 +- **linux**: 恢复了对 ARM BE8 的支持,恢复了与大端 ARM 系统的兼容性。 + +[@mrmacete]: https://github.com/mrmacete diff --git a/_i18n/cn/_posts/2025-01-23-frida-16-6-5-released.markdown b/_i18n/cn/_posts/2025-01-23-frida-16-6-5-released.markdown new file mode 100644 index 00000000..bc27d51b --- /dev/null +++ b/_i18n/cn/_posts/2025-01-23-frida-16-6-5-released.markdown @@ -0,0 +1,25 @@ +--- +layout: news_item +title: 'Frida 16.6.5 发布' +date: 2025-01-23 20:47:10 +0100 +author: oleavr +version: 16.6.5 +categories: [release] +--- + +此版本在 [@kaftejiman][] 和 [@DoranekoSystems][] 的贡献下,为我们的 Linux 和 Android 支持带来了一系列改进和修复。我们还改进了通过网络与 Apple 设备通信的方式。 + +以下是新内容: + +- **linux**: 改进注入器以避免与 memfd 区域进行有风险的代码交换 (感谢 [@kaftejiman][])。Memfd 区域可能不可写,并且与常规区域不同,如果缺少可写位,`ptrace()` 也无济于事。 + +- **linux**: 放宽注入器对 Android 的 libc 匹配 (感谢 [@kaftejiman][])。这意味着我们仍然可以将它们与绑定挂载的 APEX 匹配。 + +- **linux**: 针对 Linux/Android 优化 `NativePointer#readVolatile()` (JS) / `gum_memory_read()` (C) (感谢 [@DoranekoSystems][])。通过利用 `process_vm_readv()` (如果内核支持),我们可以避免解析内存映射。这意味着它现在只比直接访问慢约 1.45 倍,而不是慢 1000 倍以上。 + +- **fruity**: 支持 `CoreDevice` 的网络 lockdown。我们需要提供远程解锁主机密钥作为 `RSDCheckin` 的一部分。感谢 [@as0ler][] 和 [@mrmacete][] 报告并帮助弄清这个问题。 + +[@kaftejiman]: https://github.com/kaftejiman +[@DoranekoSystems]: https://github.com/DoranekoSystems +[@as0ler]: https://github.com/as0ler +[@mrmacete]: https://github.com/mrmacete diff --git a/_i18n/cn/_posts/2025-01-27-frida-16-6-6-released.markdown b/_i18n/cn/_posts/2025-01-27-frida-16-6-6-released.markdown new file mode 100644 index 00000000..6d641068 --- /dev/null +++ b/_i18n/cn/_posts/2025-01-27-frida-16-6-6-released.markdown @@ -0,0 +1,20 @@ +--- +layout: news_item +title: 'Frida 16.6.6 发布' +date: 2025-01-27 20:54:40 +0100 +author: oleavr +version: 16.6.6 +categories: [release] +--- + +此版本带来了重要的错误修复,并优化了 Linux 和 Android 上的易失性内存写入。非常感谢 [@DoranekoSystems][] 的贡献。 + +- **fruity**: 修复了上一版本中引入的 CoreDevice 上的 lockdown 回归,其中 `RSDCheckin` 现在包含一个 `EscrowBag` 以支持具有 `com.apple.crashreportmover` 等服务的网络 lockdown。结果发现这破坏了对某些缺乏与 `AppleKeyStoreUserClient` 对话权限的服务的支持。我们现在维护一个此类服务的列表,以便为它们省略 `EscrowBag`。感谢 [@as0ler][] 报告并帮助排除故障。 + +- **darwin**: 修复 Apple Silicon 上的 sysroot 检测,以便我们可以正确解析 Simulator 进程内的模块。感谢 [@stacksmashing][] 的报告。 + +- **linux**: 针对 Linux/Android 优化 `NativePointer#writeVolatile()` (JS) / `gum_memory_write()` (C) (感谢 [@DoranekoSystems][])。通过利用 `process_vm_writev()` (如果内核支持),我们可以避免解析内存映射。这意味着它现在的速度快了数千倍。 + +[@DoranekoSystems]: https://github.com/DoranekoSystems +[@as0ler]: https://github.com/as0ler +[@stacksmashing]: https://x.com/ghidraninja diff --git a/_i18n/cn/_posts/2025-03-13-frida-16-7-0-released.markdown b/_i18n/cn/_posts/2025-03-13-frida-16-7-0-released.markdown new file mode 100644 index 00000000..2bc61c63 --- /dev/null +++ b/_i18n/cn/_posts/2025-03-13-frida-16-7-0-released.markdown @@ -0,0 +1,176 @@ +--- +layout: news_item +title: 'Frida 16.7.0 发布' +date: 2025-03-13 19:09:52 +0100 +author: oleavr +version: 16.7.0 +categories: [release] +--- + +在插桩软件时,一个具有挑战性的方面是处理事物的动态性质,从线程启动和终止,到模块加载和卸载。 + +例如,如果您使用 Stalker 跟踪正在执行的线程,这在考虑插桩本身之前就提出了一些基本挑战。 + +## 哪些线程? + +虽然您*可以*使用 Interceptor 在某处放置内联 hook,以便当线程执行某些有趣的操作时,您再使用 Stalker 跟踪其执行,但有时您宁愿调用 Process.enumerateThreads() 并跟踪您认为有趣的线程。 + +每个线程可能有一个您可以使用的 `name`,但如果没有,您通常只能选择更模糊的选项。您可能会查看 `context` 属性提供的 CPU 寄存器,或者将其传递给 Thread.backtrace() 以对其进行“指纹识别”,或者您可能会查看在特定操作期间哪些线程花费了最多的 CPU 时间。 + +但是,如果您能找出线程的入口点例程和参数呢?现在您可以了: + +{% highlight sh %} +$ frida -p 163431 +Local::PID::163431 ]-> Process.enumerateThreads() +[ + … + { + "id": 163560, + "name": "SDLAudioP1", + "state": "waiting", + "context": { … }, + "entrypoint": { + "parameter": "0x561210844900", + "routine": "0x7fc7781237c0" + } + } +] +{% endhighlight %} + +## 未来的线程 + +接下来是跟踪尚未启动的线程的挑战。到目前为止,这需要 hook 特定于操作系统的内部结构。对于跨平台代理来说,这是相当大的维护复杂性。 + +我很高兴地宣布,我们现在为此提供了一个 API: + +{% highlight js %} +const observer = Process.attachThreadObserver({ + onAdded(thread) { + … + }, + onRemoved(thread) { + … + }, + onRenamed(thread, previousName) { + … + } +}); +{% endhighlight %} + +`onAdded` 回调会立即随所有现有线程一起调用,因此可以轻松管理初始状态与更新,而无需担心竞争条件。当随全新线程调用时,调用是从该新线程同步发生的。所以这是 Stalker.follow() 它的完美位置,这样你就不会错过早期执行的任何指令。 + +相反,`onRemoved` 回调告诉您线程何时即将终止。调用是从该线程同步发生的,因此您仍然有机会在该线程的上下文中执行一些最终代码。 + +最后但并非最不重要的一点是,`onRenamed` 回调告诉您线程的 `name` 何时刚刚更改,以及它的前一个名称(如果有),如果没有则为 `null`。 + +所有回调都是可选的,但必须至少提供一个。 + +然后,如果您稍后想停止观察,您需要做的就是: + +{% highlight js %} +observer.detach(); +{% endhighlight %} + +## 未来的模块 + +就像线程来来去去一样,模块/共享库也是如此。您可能会尽早应用您的插桩,以免错过早期活动。但是您越早应用插桩,应用程序的其他部分尚未加载的可能性就越大。 + +虽然卸载实际上可能不会发生,或者是因为应用程序不这样做,或者是因为动态加载器不支持它,但这是您可能必须处理的另一个方面。 + +到目前为止,处理所有这些都需要 hook 特定于操作系统的内部结构,这就带来了维护此类跨平台代理代码的所有复杂性。 + +我很高兴地分享,我们现在也为此提供了一个 API: + +{% highlight js %} +const observer = Process.attachModuleObserver({ + onAdded(module) { + … + }, + onRemoved(module) { + … + } +}); +{% endhighlight %} + +就像 Process.attachThreadObserver() 一样,`onAdded` 回调会立即随所有现有模块一起调用,因此可以轻松管理初始状态与更新,而无需担心竞争条件。当随全新模块调用时,调用会在该模块加载后立即同步发生,但在应用程序有机会使用它之前。这意味着这是应用插桩的好时机,例如使用 Interceptor。 + +相反,`onRemoved` 回调告诉您模块何时消失。 + +两个回调都是可选的,但必须至少提供一个。 + +然后,就像线程观察者 API 一样,如果您稍后想停止观察,您需要做的就是: + +{% highlight js %} +observer.detach(); +{% endhighlight %} + +## 分析代码 + +Gum(Frida 核心的 C 库)中一个鲜为人知的功能是它的名为 gum-prof 的库。它为分析代码提供了一些轻量级的构建块。从这个版本开始,我们终于将它们暴露给了 JavaScript。 + +让我们从主要组件 Profiler API 开始。它是建立在 Interceptor 之上的一个简单的最坏情况分析器: + +{% highlight js %} +const profiler = new Profiler(); +const sampler = new BusyCycleSampler(); +for (const e of Process.getModuleByName('app-core.so') + .enumerateExports() + .filter(e => e.type === 'function')) { + profiler.instrument(e.address, sampler); +} +{% endhighlight %} + +与以特定频率对调用堆栈进行采样的传统分析器不同,您可以决定您有兴趣分析的确切函数。这就是事情变得有趣的地方。 + +当这些函数中的任何一个被调用时,分析器会在进入时抓取一个样本,在返回时抓取另一个样本。然后它减去这两个值来计算调用的代价。如果结果值大于它之前为特定函数看到的值,则该值将成为其新的最坏情况。 + +每当发现新的最坏情况时,知道大部分时间/周期/等都花在特定函数上并不一定足够。例如,该函数可能仅在某些输入参数下很慢。 + +这是一种情况,您可以在调用 `instrument()` 时为特定函数传入 `describe()` 回调。您的回调应该从参数列表和/或其他状态捕获相关上下文,并返回描述刚刚发现的新最坏情况的字符串。 + +当您稍后决定调用 `generateReport()` 时,您会发现计算出的描述嵌入在每个最坏情况条目中。 + +## 采样器 + +正如您在我们刚刚接触的 Profiler 示例代码中可能已经注意到的那样,我们现在也有了“采样器”的概念。我们实际上有六种不同的实现。它们的共同点是它们实现了一个方法 `sample()`,该方法返回表示最新测量值的 bigint。它表示什么取决于特定的采样器,但对 Profiler 来说这并不重要,因为它只关心两点之间的变化量。 + +但是,这些采样器也旨在直接用于其他目的。 + +这些是全新的采样器: + +- `CycleSampler`: 测量 CPU 周期,例如在 x86 上使用 RDTSC 指令 +- `BusyCycleSampler`: 测量仅由当前线程花费的 CPU 周期,例如在 Windows 上使用 QueryThreadCycleTime() +- `WallClockSampler`: 测量时间的流逝 +- `UserTimeSampler`: 测量特定线程在用户空间中花费的时间 +- `MallocCountSampler`: 计算调用 malloc()、calloc() 和 realloc() 的次数 +- `CallCountSampler`: 计算调用您选择的函数的次数 + +关于如何使用 `UserTimeSampler` 的一个很酷的例子是用线程 ID 构造它,这意味着它将测量该特定线程在用户空间中花费的时间。通过为每个线程构造一个这样的采样器,并从每个采样器收集一个样本,然后您可以以某种特定方式运行应用程序,例如确保它被馈送特定的网络数据包。然后您将从每个采样器收集第二个样本,减去前一个样本以计算变化量/增量。这告诉您哪个线程在用户空间中花费的时间最多,因此您知道您可能想要 Stalker.follow() 哪个线程来进行近距离研究。 + +## EOF + +还有大量其他令人兴奋的更改,所以一定要查看下面的变更日志。 + +感谢 [@hsorbo][] 在线程和模块观察者功能的随机部分进行有趣且富有成效的结对编程!🙌 感谢 [@mrmacete][] 和 [@as0ler][] 帮助测试和消除错误 🥳 + +享受吧! + +## 变更日志 + +- 引入 `Process.attachThreadObserver()` 和 `ThreadRegistry` 用于监控线程创建、终止和重命名。 +- 引入 `Process.attachModuleObserver()` 和 `ModuleRegistry` 用于监控模块加载和卸载。 +- gumjs: 将 Gum 的 Profiler 和 Sampler API 暴露给 JavaScript。 +- gumjs: 添加 `NativePointer#writeVolatile()` API。感谢 [@DoranekoSystems][]! +- fruity: 修复 Linux `getifaddrs()` 逻辑中的崩溃,其中没有地址的接口未被正确处理。 +- memory-access-monitor: 提供对线程 ID 和寄存器的访问。 +- darwin: 修复拆卸期间的竞争性内存泄漏。 +- linux: 避免注入期间出现虚假的 .so 范围。 +- linux: 处理注入期间的兼容范围。 +- server: 添加 --device 用于服务特定设备。 +- compiler: 将 `@types/frida-gum` 升级到 18.8.0。 + + +[@DoranekoSystems]: https://github.com/DoranekoSystems +[@hsorbo]: https://twitter.com/hsorbo +[@mrmacete]: https://twitter.com/mrmacete +[@as0ler]: https://github.com/as0ler diff --git a/_i18n/cn/_posts/2025-03-21-frida-16-7-1-released.markdown b/_i18n/cn/_posts/2025-03-21-frida-16-7-1-released.markdown new file mode 100644 index 00000000..b4af18de --- /dev/null +++ b/_i18n/cn/_posts/2025-03-21-frida-16-7-1-released.markdown @@ -0,0 +1,41 @@ +--- +layout: news_item +title: 'Frida 16.7.1 发布' +date: 2025-03-21 13:57:25 +0100 +author: oleavr +version: 16.7.1 +categories: [release] +--- + +我很高兴宣布 Frida 16.7.1 的发布!在此版本中,我们一直忙于改进对各种架构的支持并修复一些棘手的错误。非常感谢 [@jpstotz][] 和 [@philippmao][] 的宝贵贡献! + +**主要亮点包括:** + +- **fruity**: 通过跳过具有空 UDID 的设备,修复了 Windows 上的 `Input/Output Error`。(感谢 [@jpstotz][]) + +- **droidy**: 通过增加消息大小限制,添加了对超过 ~8 个 ADB 连接设备的支持。(感谢 [@philippmao][]) + +- **thumb-relocator**: 通过在 `can_relocate()` 中利用 LLD 对齐填充,提高了在 Android 上 hook 现代工具链生成的微小函数时的成功率。 + +- **thumb-relocator**: 通过确保最后一条指令在四字节边界上并且是两字节指令,限制了填充检测。 + +- **module-registry**: 修复了微小 ELF 通知程序的 hook,方法是在 hook 之前填充注册表,允许 `CodeAllocator` 定位附近的 ELF 头。 + +- **ci**: 将 `arm64be`、`armbe8` 和 `armhf-musl` 添加到 Linux CI。 + +- **env**: 启用了在 32 位 ARM 上生成 Thumb 代码以获得更小的二进制文件。 + +- **linux**: 为 32 位 ARM 上的 musl 添加了 pthread 探测。 + +- **build**: 修复了 musl 的 `armhf` 三元组解析。 + +- **devkit**: 也为 Gum devkit 定义了 `GUM_STATIC`,因此消费者不必定义它。 + +- **devkit-assets**: 现代化了 Gum 示例。 + +- **compiler**: 将 `@types/frida-gum` 升级到 18.8.1。 + +一如既往,祝黑客愉快! + +[@jpstotz]: https://github.com/jpstotz +[@philippmao]: https://github.com/philippmao diff --git a/_i18n/cn/_posts/2025-03-21-frida-16-7-2-released.markdown b/_i18n/cn/_posts/2025-03-21-frida-16-7-2-released.markdown new file mode 100644 index 00000000..4f14e4d6 --- /dev/null +++ b/_i18n/cn/_posts/2025-03-21-frida-16-7-2-released.markdown @@ -0,0 +1,14 @@ +--- +layout: news_item +title: 'Frida 16.7.2 发布' +date: 2025-03-21 14:21:02 +0100 +author: oleavr +version: 16.7.2 +categories: [release] +--- + +同一天发布的另一个快速错误修复版本——事实证明软件很难! + +我卷起袖子修复了以下问题: + +- droidy: 修复 `MAX_MESSAGE_LENGTH` 声明。新的最大值超出了 `uint16` 的范围。 diff --git a/_i18n/cn/_posts/2025-03-21-frida-16-7-3-released.markdown b/_i18n/cn/_posts/2025-03-21-frida-16-7-3-released.markdown new file mode 100644 index 00000000..f9fcc7e6 --- /dev/null +++ b/_i18n/cn/_posts/2025-03-21-frida-16-7-3-released.markdown @@ -0,0 +1,14 @@ +--- +layout: news_item +title: 'Frida 16.7.3 发布' +date: 2025-03-21 15:43:12 +0100 +author: oleavr +version: 16.7.3 +categories: [release] +--- + +好吧,有时软件很难。这是一个修复我们 CI 的快速更新: + +- ci: 暂时从 package-linux 作业中删除 arm64beilp32。由于某些组件尚未移植到此架构,我们将暂停将其包含在我们的 Linux 包中,直到移植工作完成。 + +- ci: 将 pypa/gh-action-pypi-publish 升级到最新的 v1。 diff --git a/_i18n/cn/_posts/2025-03-31-frida-16-7-4-released.markdown b/_i18n/cn/_posts/2025-03-31-frida-16-7-4-released.markdown new file mode 100644 index 00000000..f21557fb --- /dev/null +++ b/_i18n/cn/_posts/2025-03-31-frida-16-7-4-released.markdown @@ -0,0 +1,22 @@ +--- +layout: news_item +title: 'Frida 16.7.4 发布' +date: 2025-03-31 21:23:04 +0200 +author: oleavr +version: 16.7.4 +categories: [release] +--- + +此版本包含多项改进和修复,包括对远程服务和通道的支持,以及各种其他增强功能: + +- **Core**: + - 添加了对主机会话中远程服务和通道的支持,允许 `ControlService`/`frida-server` 提供服务会话和通道。 + - 修复了 `ControlService` 中远程设备的会话逻辑。 + +- **Compiler**: + - 将 `frida-compile` 和 `@types/frida-gum` 升级到最新版本。 + +- **Python Bindings**: + - 修复了 `IOStream.read_all()` 流结束处理。 + +一如既往,祝黑客愉快! diff --git a/_i18n/cn/_posts/2025-04-05-frida-16-7-5-released.markdown b/_i18n/cn/_posts/2025-04-05-frida-16-7-5-released.markdown new file mode 100644 index 00000000..cdc8937d --- /dev/null +++ b/_i18n/cn/_posts/2025-04-05-frida-16-7-5-released.markdown @@ -0,0 +1,27 @@ +--- +layout: news_item +title: 'Frida 16.7.5 发布' +date: 2025-04-05 08:52:28 +0200 +author: oleavr +version: 16.7.5 +categories: [release] +--- + +我们很高兴宣布 Frida 16.7.5,它改进了我们的构建系统和 API,并修复了 Darwin 平台的关键问题。以下是新内容: + +- **Darwin**: 修复 `find_module_by_address` 中的双重释放。(感谢 [@mrmacete][]。) + +- **Darwin**: 更新线程列表指针雕刻以支持最近的 iOS 版本。在这些版本中,`pthread_from_mach_thread_np()` 中的线程列表指针通过 `ADRP + LDR` 而不是 `ADRP + ADD` 引用。(感谢 [@mrmacete][] 的贡献。) + +- **API**: 修复密封类的 VAPI 条目。 + +- **API**: 删除一些意外公开的类型。 + +- **API**: 密封不打算被子类化的类,防止意外的子类化。 + +- **构建系统**: 为方便起见提供 `Gio-2.0.gir`,因此用户在构建语言绑定并将 Frida 作为子项目使用时不需要安装任何 GObject Introspection 包。 + +- **构建系统**: 修复未安装情况下的 `frida_girdir`。它现在正确指向包含精炼 `.gir` 文件的目录,而不是来自 Vala 编译器的原始文件。 + + +[@mrmacete]: https://twitter.com/bezjaje diff --git a/_i18n/cn/_posts/2025-04-05-frida-16-7-6-released.markdown b/_i18n/cn/_posts/2025-04-05-frida-16-7-6-released.markdown new file mode 100644 index 00000000..5924595e --- /dev/null +++ b/_i18n/cn/_posts/2025-04-05-frida-16-7-6-released.markdown @@ -0,0 +1,12 @@ +--- +layout: news_item +title: 'Frida 16.7.6 发布' +date: 2025-04-05 11:10:47 +0200 +author: oleavr +version: 16.7.6 +categories: [release] +--- + +谁会想到软件这么难?这是另一个快速修复,以保持事情顺利运行: + +- qnx: 解封 Qinjector 以修复编译。C 胶水代码目前依赖于它的字段。 diff --git a/_i18n/cn/_posts/2025-04-05-frida-16-7-7-released.markdown b/_i18n/cn/_posts/2025-04-05-frida-16-7-7-released.markdown new file mode 100644 index 00000000..f7f77b3c --- /dev/null +++ b/_i18n/cn/_posts/2025-04-05-frida-16-7-7-released.markdown @@ -0,0 +1,12 @@ +--- +layout: news_item +title: 'Frida 16.7.7 发布' +date: 2025-04-05 14:47:13 +0200 +author: oleavr +version: 16.7.7 +categories: [release] +--- + +好吧,软件很难,不是吗?这是一个解决编译问题的快速修复版本: + +- freebsd: 解封 Binjector 以修复编译。C 胶水代码目前依赖于它的字段。 diff --git a/_i18n/cn/_posts/2025-04-07-frida-16-7-8-released.markdown b/_i18n/cn/_posts/2025-04-07-frida-16-7-8-released.markdown new file mode 100644 index 00000000..c14a978f --- /dev/null +++ b/_i18n/cn/_posts/2025-04-07-frida-16-7-8-released.markdown @@ -0,0 +1,17 @@ +--- +layout: news_item +title: 'Frida 16.7.8 发布' +date: 2025-04-07 10:09:08 +0200 +author: oleavr +version: 16.7.8 +categories: [release] +--- + +快速的错误修复版本,用于修复 Apple 操作系统上的崩溃。 + +感谢 [@mrmacete][] 贡献了以下修复: + +- darwin: 修复 find_module_by_address 中的模块类型。 + 这是一个导致微妙崩溃的类型混淆。 + +[@mrmacete]: https://twitter.com/bezjaje diff --git a/_i18n/cn/_posts/2025-04-07-frida-16-7-9-released.markdown b/_i18n/cn/_posts/2025-04-07-frida-16-7-9-released.markdown new file mode 100644 index 00000000..4f61b5d2 --- /dev/null +++ b/_i18n/cn/_posts/2025-04-07-frida-16-7-9-released.markdown @@ -0,0 +1,15 @@ +--- +layout: news_item +title: 'Frida 16.7.9 发布' +date: 2025-04-07 19:32:56 +0200 +author: oleavr +version: 16.7.9 +categories: [release] +--- + +事实证明软件很难!在我们上一个版本发布的同一天,我们推出了另一个快速的错误修复版本来解决出现的一些问题。非常感谢 [@mrmacete][] 的贡献。以下是新内容: + +- **channel**: 在空缓冲区上中断读取循环。(感谢 [@mrmacete][]!) +- **device-manager**: 修复拆卸逻辑,在 `HostSessionService` 尚未 `start()` 的情况下,我们也将会 `stop()` 它。 + +[@mrmacete]: https://twitter.com/bezjaje diff --git a/_i18n/cn/_posts/2025-04-09-frida-16-7-10-released.markdown b/_i18n/cn/_posts/2025-04-09-frida-16-7-10-released.markdown new file mode 100644 index 00000000..0e419f66 --- /dev/null +++ b/_i18n/cn/_posts/2025-04-09-frida-16-7-10-released.markdown @@ -0,0 +1,21 @@ +--- +layout: news_item +title: 'Frida 16.7.10 发布' +date: 2025-04-09 21:50:31 +0200 +author: oleavr +version: 16.7.10 +categories: [release] +--- + +我们很高兴宣布 Frida 16.7.10,带来了几个稳定性改进。特别感谢 [@mrmacete][] 确定了所有这些问题的根本原因,并修复了 Apple 操作系统上的死锁。 + +此版本包含以下更改: + +- **network-stack**: 修复了 TcpConnection 传输逻辑。 +- **network-stack**: 改进了 VirtualStream 锁定以增强批处理并避免在 Frida 线程上进行不必要的调度。 +- **virtual-stream**: 允许在不持有锁的情况下调用 `update_pending_io()`,以便在子类在调用它之前不需要更新任何其他状态时提供便利。我们的一个子类假设情况就是这样,因为这是在共性被分解到 VirtualStream 基类之前的行为。这意味着 16.7.4 中进行的重构引入了竞争条件,此更改修复了它。 +- **darwin**: 通过避免标志优化了线程枚举。这消除了堆分配,降低了死锁风险,例如在 Interceptor 中,线程枚举用于按 ID 挂起和恢复线程。(由 [@mrmacete][] 贡献。) + +享受吧! + +[@mrmacete]: https://twitter.com/bezjaje diff --git a/_i18n/cn/_posts/2025-04-14-frida-16-7-11-released.markdown b/_i18n/cn/_posts/2025-04-14-frida-16-7-11-released.markdown new file mode 100644 index 00000000..350bc8ca --- /dev/null +++ b/_i18n/cn/_posts/2025-04-14-frida-16-7-11-released.markdown @@ -0,0 +1,23 @@ +--- +layout: news_item +title: 'Frida 16.7.11 发布' +date: 2025-04-14 17:59:58 +0200 +author: oleavr +version: 16.7.11 +categories: [release] +--- + +快速的错误修复版本,改进了我们的 Fruity 后端,并基本支持 iOS 18.4: + +- darwin: 更新针对 iOS 18.4 的注入器 dyld init 检测。 + +- fruity: 更新针对 iOS 18.4 的注入器 dyld init 检测 (#1154)。感谢 [@pachoo][] 的贡献。 + +- network-stack: 减少写入之间的延迟。通过一旦有一些空间可用就标记为可写,而无需等待低水位线。感谢 [@mrmacete][] 的改进。 + +- network-stack: 修复 TCP 写入块长度 (#1156)。[@mrmacete][] 的另一个修复。 + +- network-stack: 始终发送排队的数据。此更改确保即使队列上没有更多可用空间,也会调用 `pcb.output()`,从而防止停顿。归功于 [@mrmacete][] 防止了那些讨厌的停顿。 + +[@mrmacete]: https://twitter.com/bezjaje +[@pachoo]: https://github.com/pachoo diff --git a/_i18n/cn/_posts/2025-04-16-frida-16-7-12-released.markdown b/_i18n/cn/_posts/2025-04-16-frida-16-7-12-released.markdown new file mode 100644 index 00000000..cb7b4605 --- /dev/null +++ b/_i18n/cn/_posts/2025-04-16-frida-16-7-12-released.markdown @@ -0,0 +1,17 @@ +--- +layout: news_item +title: 'Frida 16.7.12 发布' +date: 2025-04-16 20:26:58 +0200 +author: oleavr +version: 16.7.12 +categories: [release] +--- + +此版本带来了几个重要的改进和修复: + +- interceptor: 避免挂起期间的堆分配。通过将挂起的线程 ID 存储到 `GumMetalArray` 而不是 `GQueue` 中,我们防止了当挂起的线程持有 `dlmalloc` 锁时发生死锁。感谢 [@mrmacete][] 的改进。 +- linux: 修复 ELF 丢失时的 `ModuleRegistry` 初始化。当枚举 RTLD 通知程序和已加载模块以查找 `DT_DEBUG` 条目时,我们不再假设 ELF 文件存在。这修复了程序从 memfd 加载并在没有文件的情况下生成时的问题。 +- elf-module: 如果无法映射文件,则使用实时内存。对于由 memfd 支持的模块,如果我们无法映射文件,我们现在利用内存中的 ELF 而不是放弃。 +- linux: 重做 glibc pthread 内部检测。我们不再解析机器代码来确定内部结构,而是启动线程以更稳健的方式解决难题。 + +[@mrmacete]: https://twitter.com/bezjaje diff --git a/_i18n/cn/_posts/2025-04-17-frida-16-7-13-released.markdown b/_i18n/cn/_posts/2025-04-17-frida-16-7-13-released.markdown new file mode 100644 index 00000000..210f9165 --- /dev/null +++ b/_i18n/cn/_posts/2025-04-17-frida-16-7-13-released.markdown @@ -0,0 +1,12 @@ +--- +layout: news_item +title: 'Frida 16.7.13 发布' +date: 2025-04-17 13:53:57 +0200 +author: oleavr +version: 16.7.13 +categories: [release] +--- + +快速的错误修复版本,解决了我们的 ELF 解析器中的一个问题: + +- elf-module: 修复回退到实时内存时 check_str_bounds() 中的递归。该函数假设有文件支持的 ELF;回退到实时内存可能会触发无限递归。已添加显式实时内存检查以返回错误。 diff --git a/_i18n/cn/_posts/2025-04-25-frida-16-7-14-released.markdown b/_i18n/cn/_posts/2025-04-25-frida-16-7-14-released.markdown new file mode 100644 index 00000000..a8a07cb3 --- /dev/null +++ b/_i18n/cn/_posts/2025-04-25-frida-16-7-14-released.markdown @@ -0,0 +1,22 @@ +--- +layout: news_item +title: 'Frida 16.7.14 发布' +date: 2025-04-25 22:36:07 +0200 +author: oleavr +version: 16.7.14 +categories: [release] +--- + +此版本改进了对 32 位 ARM 上 Google 最新 ART 运行时的支持,改善了 Linux 上的错误处理,并改进了对大端 ARM 架构的支持。 + +以下是亮点: + +- **改进 frida-java-bridge 以支持 Google 最新的 32 位 ARM 二进制文件。** 感谢 [@Rwkeith][]。 +- **在 Linux 后端失败时传播 ptrace 错误。** 感谢 [@DoranekoSystems][]。 +- **改进对大端 ARM 架构 (armbe8, arm64be, arm64beilp32) 的支持。** +- **删除了 Linux 后端中多余的硬件断点代码。** + +一如既往,我们感谢贡献者为使 Frida 变得更好而付出的宝贵努力。 + +[@Rwkeith]: https://github.com/Rwkeith +[@DoranekoSystems]: https://github.com/DoranekoSystems diff --git a/_i18n/cn/_posts/2025-05-08-frida-16-7-15-released.markdown b/_i18n/cn/_posts/2025-05-08-frida-16-7-15-released.markdown new file mode 100644 index 00000000..4fe88ec0 --- /dev/null +++ b/_i18n/cn/_posts/2025-05-08-frida-16-7-15-released.markdown @@ -0,0 +1,19 @@ +--- +layout: news_item +title: 'Frida 16.7.15 发布' +date: 2025-05-08 21:31:10 +0200 +author: oleavr +version: 16.7.15 +categories: [release] +--- + +我们很高兴宣布我们的 Node.js 绑定的重大更新!它们已从头开始重写。我们的绑定现在针对 Node-API,而不是依赖于在版本之间不可避免地更改的 Node.js 和 V8 API(需要每个 ABI 单独构建)。这意味着单个二进制文件可用于所有现代版本的 Node.js、Electron、nw.js 等。我们只需要为每个 OS/arch/libc 组合构建一个二进制文件。不仅如此,我们的新绑定也是自动生成的,确保 frida-core 中的 API 更改立即反映出来,如果适用,只需要调整自定义。 + +此版本还包含其他一些好东西: + +- *DeviceManager*: 修复了在 `HostSessionService` `start()` 被取消的情况下早期 `close()` 时的崩溃。 +- *Web Service*: 修复了 TLS 连接的处理,因为远程地址逻辑没有考虑到它们。 +- *API*: 修复了 `.gir` 文件中的 `FridaBase` 引用。 +- *Bus*: 删除了 `device` 属性,该属性不打算公开。 + +一如既往,非常感谢所有贡献者和用户的支持。 diff --git a/_i18n/cn/_posts/2025-05-08-frida-16-7-16-released.markdown b/_i18n/cn/_posts/2025-05-08-frida-16-7-16-released.markdown new file mode 100644 index 00000000..4793a2db --- /dev/null +++ b/_i18n/cn/_posts/2025-05-08-frida-16-7-16-released.markdown @@ -0,0 +1,12 @@ +--- +layout: news_item +title: 'Frida 16.7.16 发布' +date: 2025-05-08 22:43:23 +0200 +author: oleavr +version: 16.7.16 +categories: [release] +--- + +另一个快速的错误修复版本,因为软件很难!此版本解决了以下问题: + +- node: 修复非从源代码构建时的模块解析。事实证明,在我们构建目录中生成的 `package.json` 混淆了 `bindings` 包中的模块根检测。这意味着我们的搜索路径实际上是错误的,并且依赖于这种误检。 diff --git a/_i18n/cn/_posts/2025-05-09-frida-16-7-17-released.markdown b/_i18n/cn/_posts/2025-05-09-frida-16-7-17-released.markdown new file mode 100644 index 00000000..c04ddb83 --- /dev/null +++ b/_i18n/cn/_posts/2025-05-09-frida-16-7-17-released.markdown @@ -0,0 +1,17 @@ +--- +layout: news_item +title: 'Frida 16.7.17 发布' +date: 2025-05-09 11:56:06 +0200 +author: oleavr +version: 16.7.17 +categories: [release] +--- + +此版本为我们的 Fruity 后端带来了一些改进和修复,增强了跨平台的兼容性和错误处理。以下是此版本中包含的更改: + +- fruity: 如果需要,也在 macOS 上启动配对。 +- fruity: 调整 macOS CreateAssertion 错误处理。所以当它失败时我们提供一个干净的错误消息。 +- fruity: 处理 OPACK 布尔值和紧凑整数。iOS 18.4.1 上的配对期间需要对后者的支持。 +- fruity: 改进便携式隧道错误处理。当我们使用 lockdown 来确定 iDevice 是否支持隧道,并且由于缺乏 lockdown 级配对而失败时,只需假设 OS 支持它。 +- fruity: 为 Linux netif 等待逻辑添加超时。 +- fruity: 处理 NetworkManager DISCONNECTED+CARRIER。所以当设备快速通过 state=DISCONNECTED 且 reason=CARRIER 时,我们实际上会等到设备准备好。 diff --git a/_i18n/cn/_posts/2025-05-09-frida-16-7-18-released.markdown b/_i18n/cn/_posts/2025-05-09-frida-16-7-18-released.markdown new file mode 100644 index 00000000..a122fe54 --- /dev/null +++ b/_i18n/cn/_posts/2025-05-09-frida-16-7-18-released.markdown @@ -0,0 +1,15 @@ +--- +layout: news_item +title: 'Frida 16.7.18 发布' +date: 2025-05-09 15:00:00 +0200 +author: oleavr +version: 16.7.18 +categories: [release] +--- + +背靠背发布!软件很难,有时我们需要推送快速修复以保持事情顺利运行。以下是此版本的新内容: + +- **frida-node** 修复了一个回归,其中 `Device.openChannel()` 返回类型与之前相比发生了变化。 +- **Fruity 后端改进**: 使用 Apple 的 CoreDeviceProxy 作为后备,以便我们可以支持 NCM 有问题的系统。另外每 250 毫秒重新传输 mDNS-SD 请求,以增加稳健性。 + +祝黑客愉快! diff --git a/_i18n/cn/_posts/2025-05-14-frida-16-7-19-released.markdown b/_i18n/cn/_posts/2025-05-14-frida-16-7-19-released.markdown new file mode 100644 index 00000000..6c12fec7 --- /dev/null +++ b/_i18n/cn/_posts/2025-05-14-frida-16-7-19-released.markdown @@ -0,0 +1,15 @@ +--- +layout: news_item +title: 'Frida 16.7.19 发布' +date: 2025-05-14 15:27:00 +0200 +author: oleavr +version: 16.7.19 +categories: [release] +--- + +此版本改进了 glibc 下的 Linux 线程枚举,并加强了我们的静态 OpenSSL 构建以防止意外的配置加载。 + +**变化:** +- **linux**: 跳过 `tid = 0` 的 glibc 线程(已退出但未加入),以避免错误的“不支持的 Linux 系统”恐慌。 +- **linux**: 按创建顺序列出 glibc 线程(最旧的在前),以反映真实的线程生命周期。 +- **deps (TLS)**: 使用 `OPENSSL_NO_AUTOLOAD_CONFIG` 构建 OpenSSL 以阻止加载任何系统配置文件,防止初始化 TLS 时崩溃。 diff --git a/_i18n/cn/_posts/2025-05-17-frida-17-0-0-released.markdown b/_i18n/cn/_posts/2025-05-17-frida-17-0-0-released.markdown new file mode 100644 index 00000000..93c6cc16 --- /dev/null +++ b/_i18n/cn/_posts/2025-05-17-frida-17-0-0-released.markdown @@ -0,0 +1,177 @@ +--- +layout: news_item +title: 'Frida 17.0.0 发布' +date: 2025-05-17 19:45:49 +0200 +author: oleavr +version: 17.0.0 +categories: [release] +--- + +经过无数杯咖啡和有趣的编码会议,[@hsorbo][] 和我很高兴为您带来 Frida 17.0.0。距离上一次主要版本更新已经快三年了,在努力寻找进行破坏性更改的正确时机之后,我们决定终于到时候了。 + +## Runtime Bridges + +困扰我们很长一段时间的主要问题是我们的 runtime bridges,即 frida-{objc,swift,java}-bridge,与 Frida 的 GumJS 运行时捆绑在一起。这带来了一些主要痛点: + +- 惯性:受限于 Frida 的发布周期。 +- 臃肿:对于不需要特定 runtime bridge 的用户来说。 +- 可扩展性:我们希望看到各种运行时的桥接器,但我们添加到 Frida 的越多,我们就越会与惯性和臃肿作斗争。 +- 可发现性:社区维护的桥接器更难被发现,因为它们需要不同的消费工作流程。 + +不过我一直犹豫是否停止捆绑它们,因为要求自定义代理进行构建步骤似乎会增加太多的摩擦。想到会破坏书籍、博客文章、[CodeShare][] 等中的示例,我也感到不安。 + +摩擦方面是我们早在 [15.2][] 中引入 frida.Compiler API 的原因,同时 frida-tools 发布了一个基于它构建的 CLI 工具 frida-compile。我们的 REPL 也得到了改进,支持直接加载 .ts (TypeScript),在幕后利用 frida.Compiler。 + +但这仍然是一个额外的步骤,对于使用 Frida REPL 或 frida-trace 的一次性脚本和早期原型设计工作来说太麻烦了。而且,这会破坏很多例子。为了解决这个问题,刚刚发布的 frida-tools 14.0.0 将这三个桥接器烘焙到其 REPL 和 frida-trace 代理中。 + +我们的桥接器也已迁移到 ESM,因此它们可以被最新版本的 frida-compile 消费。(感谢 [@yotamN][] 迁移 frida-java-bridge ♥️) + +那些从源代码构建 Frida 的人可能还会注意到构建时间的改进。由于我们不再捆绑桥接器,我们终于可以摆脱 Gum 的 frida-compile 依赖,并停止让 Gum 本身依赖 Node.js + npm。 + +我们仍然有 GumJS 自己的运行时,它实现了诸如 `console.log()` 之类的内置函数,但将其移植到 ESM 并简单地单独烘焙每个模块意味着我们不再需要 JavaScript 打包器。这意味着 Gum 本身的构建时间更快:在 Linux 驱动的 i9-12900K 系统上,构建时间从 ~24s 降至 ~6s。 + +您可以在 [bridges][] 中查看快速参考教程。 + +## 传统风格的枚举 API + +以前,我们的同步枚举 API 看起来像这样: + +{% highlight javascript %} +Process.enumerateModules({ + onMatch(module) { + console.log(module.name); + }, + onComplete() { + } +}); +{% endhighlight %} + +还有一个等效的 **Sync** 后缀方法,例如此特定示例的 `Process.enumerateModulesSync()`。当时的想法是底层实现可能会变成异步的,但目前大多数都不是,所以 Sync 后缀的实现只是异步外观 API 的一个薄包装。 + +后来,随着支持的平台越来越多,我意识到所有伪装的异步实现结果总是快速且廉价的操作。所以提供异步风格是没有意义的。对于少数从一开始就真正异步的,比如 `Memory.scan()`,让它们保持这种状态仍然是有意义的。 + +不过我犹豫是否要破坏 API,所以我选择向每个无后缀实现添加检查,如果省略回调参数,它的行为就像其 Sync 后缀对应物一样。为了让用户迁移出旧式 API,我确保更新我们的 TypeScript 绑定,以便只包含现代风格。 + +现代风格的等效项如下所示: + +{% highlight javascript %} +for (const module of Process.enumerateModules()) { + console.log(module.name); +} +{% endhighlight %} + +其中 `Process.enumerateModules()` 返回 Module 对象数组。 + +这些传统风格的 API 现在终于消失了。那些用 TypeScript 编写代理的人不需要做任何事情,除非你使用的是我们类型定义的古老版本。 + +## 内存读/写 API + +以前,你会像这样访问内存: + +{% highlight javascript %} +const playerHealthLocation = ptr('0x1234'); +const playerHealth = Memory.readU32(playerHealthLocation); +Memory.writeU32(playerHealthLocation, 100); +{% endhighlight %} + +现代等效项是: + +{% highlight javascript %} +const playerHealthLocation = ptr('0x1234'); +const playerHealth = playerHealthLocation.readU32(); +playerHealthLocation.writeU32(100); +{% endhighlight %} + +其中每个写入对应物都返回 NativePointer 本身,以支持链式调用: + +{% highlight javascript %} +const playerData = ptr('0x1234'); +playerData + .add(4).writeU32(13) + .add(4).writeU16(37) + .add(2).writeU16(42) + ; +{% endhighlight %} + +这些的旧版本现在也消失了,并且只要传统风格的枚举 API 存在,它们就已经从我们的 TypeScript 绑定中消失了。所以这个变化对你们大多数人来说应该也不明显。 + +## 静态 Module API + +现在是破坏性更改,这也影响了在 Frida 17 发布之前使用 TypeScript 绑定的用户。以下静态 Module 方法现在已消失: + +- Module.ensureInitialized() +- Module.findBaseAddress() +- Module.getBaseAddress() +- Module.findExportByName() +- Module.getExportByName() +- Module.findSymbolByName() +- Module.getSymbolByName() + +这些都很容易迁移。 + +但首先,让我们涵盖那个奇怪的: + +{% highlight javascript %} +Module.getSymbolByName(null, 'open') +{% endhighlight %} + +现在这样完成: + +{% highlight javascript %} +Module.getGlobalExportByName('open') +{% endhighlight %} + +对于其余部分,您首先需要查找 Module,然后访问其上所需的属性或方法。例如,代替: + +{% highlight javascript %} +Module.getExportByName('libc.so', 'open') +{% endhighlight %} + +新方法是: + +{% highlight javascript %} +Process.getModuleByName('libc.so').getExportByName('open') +{% endhighlight %} + +Module.getBaseAddress() 的等效项因此是: + +{% highlight javascript %} +Process.getModuleByName('libc.so').base +{% endhighlight %} + +这意味着现在只有一种进行 Module 自省的方法,并且 API 设计鼓励您编写高性能代码。例如,过去您可能会想这样做: + +{% highlight javascript %} +const openImpl = Process.getExportByName('libc.so', 'open'); +const readImpl = Process.getExportByName('libc.so', 'read'); +{% endhighlight %} + +但现在你在做之前可能会三思: + +{% highlight javascript %} +const openImpl = Process.getModuleByName('libc.so').getExportByName('open'); +const readImpl = Process.getModuleByName('libc.so').getExportByName('read'); +{% endhighlight %} + +而是这样做: + +{% highlight javascript %} +const libc = Process.getModuleByName('libc.so'); +const openImpl = libc.getExportByName('open'); +const readImpl = libc.getExportByName('read'); +{% endhighlight %} + +这既更具可读性又更具性能。 + +最后但并非最不重要的一点是,静态枚举 API,例如 `Module.enumerateExports()`,现在也消失了。然而,这些早在很久以前就从 TypeScript 绑定中删除了,所以你们大多数人应该不需要处理这些。但是如果你这样做了,迁移看起来与上面完全相同。 + +## EOF + +差不多就是这样。祝黑客愉快! + + +[@hsorbo]: https://twitter.com/hsorbo +[CodeShare]: https://codeshare.frida.re/ +[15.2]: /news/2022/07/21/frida-15-2-0-released/ +[@yotamN]: https://github.com/yotamN +[bridges]: /docs/bridges diff --git a/_i18n/cn/_posts/2025-05-17-frida-17-0-1-released.markdown b/_i18n/cn/_posts/2025-05-17-frida-17-0-1-released.markdown new file mode 100644 index 00000000..cadb021c --- /dev/null +++ b/_i18n/cn/_posts/2025-05-17-frida-17-0-1-released.markdown @@ -0,0 +1,17 @@ +--- +layout: news_item +title: 'Frida 17.0.1 发布' +date: 2025-05-17 23:48:23 +0200 +author: oleavr +version: 17.0.1 +categories: [release] +--- + +惊喜!我们在 17.0.0 发布的同一天发布了一个快速补丁版本。事实证明软件很难! + +此版本包含以下修复: + +- **Core**: 更新接口版本以匹配主版本。 +- **Darwin**: 将 `frida-objc-bridge` 升级到版本 8.0.4。 +- **Android**: 将 `frida-java-bridge` 升级到版本 7.0.1。 +- **frida-node**: 修复 `Device.openChannel()` 的返回类型,以避免公开我们将来可能想要更改的实现细节。 diff --git a/_i18n/cn/_posts/2025-05-20-frida-17-0-2-released.markdown b/_i18n/cn/_posts/2025-05-20-frida-17-0-2-released.markdown new file mode 100644 index 00000000..0585a99d --- /dev/null +++ b/_i18n/cn/_posts/2025-05-20-frida-17-0-2-released.markdown @@ -0,0 +1,14 @@ +--- +layout: news_item +title: 'Frida 17.0.2 发布' +date: 2025-05-20 22:41:03 +0200 +author: oleavr +version: 17.0.2 +categories: [release] +--- + +此版本带来了一些错误修复: + +- **Compiler**: 将 frida-compile 和 frida-fs 更新到最新版本。 +- **gum**: 修复了与 GObject 相关的被遗忘的精简位,现在它又变回了必需的库。 +- **gumjs**: 修复了在没有断言的情况下构建时的已分配但未使用的警告。 diff --git a/_i18n/cn/_posts/2025-05-20-frida-17-0-3-released.markdown b/_i18n/cn/_posts/2025-05-20-frida-17-0-3-released.markdown new file mode 100644 index 00000000..2777d558 --- /dev/null +++ b/_i18n/cn/_posts/2025-05-20-frida-17-0-3-released.markdown @@ -0,0 +1,12 @@ +--- +layout: news_item +title: 'Frida 17.0.3 发布' +date: 2025-05-20 23:12:23 +0200 +author: oleavr +version: 17.0.3 +categories: [release] +--- + +软件很难,不是吗?紧随上一个版本之后,这是一个快速修复: + +- compiler: 更新 @frida/rollup-plugin-node-polyfills 以解决依赖问题,它仍然依赖于旧的 frida-fs。 diff --git a/_i18n/cn/_posts/2025-05-22-frida-17-0-4-released.markdown b/_i18n/cn/_posts/2025-05-22-frida-17-0-4-released.markdown new file mode 100644 index 00000000..30254af4 --- /dev/null +++ b/_i18n/cn/_posts/2025-05-22-frida-17-0-4-released.markdown @@ -0,0 +1,13 @@ +--- +layout: news_item +title: 'Frida 17.0.4 发布' +date: 2025-05-22 21:39:44 +0200 +author: oleavr +version: 17.0.4 +categories: [release] +--- + +此版本改进了 Compiler 实现,支撑 frida-tools 的一部分 frida-compile CLI 工具。以下是更改: + +- 升级到 **frida-compile 18**,现在使用 TypeScript 5.8.3,最新的 frida-fs 等。 +- 修复了 Windows 上的资产打包逻辑,其中虚拟路径使用了错误的路径分隔符进行存储。 diff --git a/_i18n/cn/_posts/2025-05-24-frida-17-0-5-released.markdown b/_i18n/cn/_posts/2025-05-24-frida-17-0-5-released.markdown new file mode 100644 index 00000000..2e0db91c --- /dev/null +++ b/_i18n/cn/_posts/2025-05-24-frida-17-0-5-released.markdown @@ -0,0 +1,10 @@ +--- +layout: news_item +title: 'Frida 17.0.5 发布' +date: 2025-05-24 21:03:32 +0200 +author: oleavr +version: 17.0.5 +categories: [release] +--- + +快速的错误修复版本,用于修复由最新 frida-compile 生成的代理。既然 TypeScript 编译器更加一致并最终生成 CommonJS 胶水代码,我们现在需要显式地将内部代理声明为 ESM。这修复了我们的 Darwin 和 Android 代理,以及 Barebone 后端的脚本运行时。 diff --git a/_i18n/cn/_posts/2025-05-28-frida-17-0-6-released.markdown b/_i18n/cn/_posts/2025-05-28-frida-17-0-6-released.markdown new file mode 100644 index 00000000..5d0b18af --- /dev/null +++ b/_i18n/cn/_posts/2025-05-28-frida-17-0-6-released.markdown @@ -0,0 +1,14 @@ +--- +layout: news_item +title: 'Frida 17.0.6 发布' +date: 2025-05-28 23:17:13 +0200 +author: oleavr +version: 17.0.6 +categories: [release] +--- + +快速的错误修复版本,包含 [@londek][] 的重要贡献。在此版本中,我们解决了以下问题: + +- **darwin**: 修复了 launchd 代理,它仍在使用已删除的旧 GumJS API。这阻止了代理在越狱的 iOS/iPadOS/tvOS 系统上运行。 + +[@londek]: https://github.com/londek diff --git a/_i18n/cn/_posts/2025-05-29-frida-17-0-7-released.markdown b/_i18n/cn/_posts/2025-05-29-frida-17-0-7-released.markdown new file mode 100644 index 00000000..72525f37 --- /dev/null +++ b/_i18n/cn/_posts/2025-05-29-frida-17-0-7-released.markdown @@ -0,0 +1,16 @@ +--- +layout: news_item +title: 'Frida 17.0.7 发布' +date: 2025-05-29 17:32:19 +0200 +author: oleavr +version: 17.0.7 +categories: [release] +--- + +此版本包含一些重要的修复: + +- **device**: 允许代理会话在释放前分离,以应对无序分离可能导致释放时无限挂起的情况。感谢 [@mrmacete][]! +- **darwin**: 处置模块解析器索引,以避免在使用短寿命解析器(例如,在不同任务上)的情况下泄漏本机模块。感谢 [@mrmacete][]! +- **stalker**: 处理没有内联缓存条目的块。 + +[@mrmacete]: https://twitter.com/bezjaje diff --git a/_i18n/cn/_posts/2025-06-05-frida-17-1-0-released.markdown b/_i18n/cn/_posts/2025-06-05-frida-17-1-0-released.markdown new file mode 100644 index 00000000..d7455ab5 --- /dev/null +++ b/_i18n/cn/_posts/2025-06-05-frida-17-1-0-released.markdown @@ -0,0 +1,31 @@ +--- +layout: news_item +title: 'Frida 17.1.0 发布' +date: 2025-06-05 22:05:33 +0200 +author: oleavr +version: 17.1.0 +categories: [release] +--- + +包含几个令人兴奋的改进的大版本发布! + +首先,我们在 Compiler 后端切换到了 ESBuild 和 typescript-go,从而大幅提高了性能,并通过不再需要维护打包器来减轻了我们的维护负担。我们还添加了配置输出和包格式的选项,并支持禁用类型检查。 + +其次,我们现在终于发布了 Windows/ARM64 的二进制文件。这是由 GitHub 向公众提供 Windows ARM64 托管运行器解锁的。 + +特别感谢 [@mrmacete][] 堵住了 Interceptor 单例泄漏,以及 [@fesily][] 实现了 Windows 上的 `Module#enumerateSections()`,以及改进了 `Module#enumerateImports()` 以公开 `slot`。 + +以下是完整的更改列表: + +- **Compiler 改进**: + - 在 Compiler 后端切换到 ESBuild 和 typescript-go。 + - 添加了配置输出和包格式的选项,并支持禁用类型检查。 +- **Windows/ARM64 支持**: + - CI 更新为发布 Windows/arm64 二进制文件。 +- **来自我们社区的贡献**: + - 修复了针对 Thumb 地址的 32 位 ARM 断点逻辑。 + - 堵住了 Interceptor 单例泄漏 ([@mrmacete][])。 + - 实现了 `Module#enumerateSections()` 并在 Windows 上连接了导入槽 ([@fesily][])。 + +[@mrmacete]: https://twitter.com/bezjaje +[@fesily]: https://github.com/fesily diff --git a/_i18n/cn/_posts/2025-06-06-frida-17-1-1-released.markdown b/_i18n/cn/_posts/2025-06-06-frida-17-1-1-released.markdown new file mode 100644 index 00000000..8ac24840 --- /dev/null +++ b/_i18n/cn/_posts/2025-06-06-frida-17-1-1-released.markdown @@ -0,0 +1,21 @@ +--- +layout: news_item +title: 'Frida 17.1.1 发布' +date: 2025-06-06 14:11:29 +0200 +author: oleavr +version: 17.1.1 +categories: [release] +--- + +喝了一杯新鲜的咖啡后,我敲定了以下改进: + +- **构建系统改进**: + - 将打包切换到 ESBuild 用于: + - Darwin 上的 `reportcrash.js`。 + - Darwin 上的 `osanalytics.js`。 + - Linux 上的 `system-server.js`。 + - Barebone 后端中的运行时。 + +- **Barebone 后端修复**: + - 修复了 ESM 处理,其中未能等待返回的 Promise 导致错误被吞没。现在,任何错误都将被正确报告。 + - 删除了陈旧的 bridge 全局变量。 diff --git a/_i18n/cn/_posts/2025-06-06-frida-17-1-2-released.markdown b/_i18n/cn/_posts/2025-06-06-frida-17-1-2-released.markdown new file mode 100644 index 00000000..28c9ed9b --- /dev/null +++ b/_i18n/cn/_posts/2025-06-06-frida-17-1-2-released.markdown @@ -0,0 +1,22 @@ +--- +layout: news_item +title: 'Frida 17.1.2 发布' +date: 2025-06-06 22:11:49 +0200 +author: oleavr +version: 17.1.2 +categories: [release] +--- + +显然,软件*是*很难的!在发布 17.1.1 仅仅几个小时后,我们又回来发布了一个后续版本,打磨了 **frida-core** 和 **frida-node**。 + +### frida-core +- **Compiler** – `DeviceManager` 构造函数参数现在是可选的。 + 它保留在签名中以保持 ABI 兼容性,但不再使用。 + +### frida-node (Node.js / N-API bindings) +- **信号处理** + - 处理程序现在正确接收 `GVariant` 参数。 + - 处理程序内部抛出的异常会传播而不是被吞没。 +- **Compiler** – 在连接任何 `output` 信号处理程序时保持运行时存活,防止过早退出。 + +祝黑客愉快! diff --git a/_i18n/cn/_posts/2025-06-07-frida-17-1-3-released.markdown b/_i18n/cn/_posts/2025-06-07-frida-17-1-3-released.markdown new file mode 100644 index 00000000..e3820c7e --- /dev/null +++ b/_i18n/cn/_posts/2025-06-07-frida-17-1-3-released.markdown @@ -0,0 +1,15 @@ +--- +layout: news_item +title: 'Frida 17.1.3 发布' +date: 2025-06-07 13:32:40 +0200 +author: oleavr +version: 17.1.3 +categories: [release] +--- + +此版本带来了各个组件的一系列改进和错误修复。以下是亮点: + +- 重命名了 Compiler 后端中冲突的 Go 符号,以避免链接到 Go 二进制文件时发生冲突。这确保了与 Go 项目集成时的兼容性。 +- 在 Linux 上,修复了与线程列表锚点相关的问题,以防止误报。以前,锚点可能会被错误地添加到线程列表中,导致不正确的行为。 +- 更正了 gadget 和 GumJS 中的 QuickJS 大端字节码检查。以前的检查在大端系统上是不正确的。 +- 向 API 添加了版本定义和宏,为开发人员提供更明确的版本信息。 diff --git a/_i18n/cn/_posts/2025-06-10-frida-17-1-4-released.markdown b/_i18n/cn/_posts/2025-06-10-frida-17-1-4-released.markdown new file mode 100644 index 00000000..83bac8d6 --- /dev/null +++ b/_i18n/cn/_posts/2025-06-10-frida-17-1-4-released.markdown @@ -0,0 +1,24 @@ +--- +layout: news_item +title: 'Frida 17.1.4 发布' +date: 2025-06-10 22:10:02 +0200 +author: oleavr +version: 17.1.4 +categories: [release] +--- + +很高兴宣布 Frida 17.1.4,它带来了几个重要的修复和改进 —— 最值得注意的是 Android 16 支持。以下是新内容: + +- **Compiler**: 将 esbuild 的 `platform` 切换为 `node`,以便 `package.json` 的 `main` 和 `exports` 以 Node.js 方式解析,恢复了依赖它的包的兼容性。感谢 [@hsorbo][] 帮助追踪此问题。 +- **Plist**: 修复了二进制属性列表的 `offsetIntSize`,确保与 Core Foundation 的兼容性。感谢 [@mrmacete][] 帮助追踪此问题。 +- **Plist**: XML 输出中的空字典和数组现在使用自闭合标签 (例如 ``),以匹配 Apple 的编码器。 +- **Android**: 将 `system_server` 代理中的 `frida-java-bridge` 升级到 7.0.3,添加了 Android 16 支持。感谢 [@tbodt][] — 并感谢 [@thinhbuzz][] 贡献了一个错误处理补丁,解决了某些 Android 12 设备上的不可操作性问题。 +- **Darwin**: 将内部代理中的 `frida-objc-bridge` 升级到 8.0.5。 +- **GumJS**: 修复了 FFI 参数的大端处理。 + +我们建议所有用户尽早升级。确保你也升级到刚刚发布的 frida-tools 14.1.2。 + +[@hsorbo]: https://twitter.com/hsorbo +[@mrmacete]: https://twitter.com/bezjaje +[@tbodt]: https://mastodon.social/@tbodt +[@thinhbuzz]: https://github.com/thinhbuzz diff --git a/_i18n/cn/_posts/2025-06-13-frida-17-1-5-released.markdown b/_i18n/cn/_posts/2025-06-13-frida-17-1-5-released.markdown new file mode 100644 index 00000000..d99d9e2a --- /dev/null +++ b/_i18n/cn/_posts/2025-06-13-frida-17-1-5-released.markdown @@ -0,0 +1,10 @@ +--- +layout: news_item +title: 'Frida 17.1.5 发布' +date: 2025-06-13 16:28:28 +0200 +author: oleavr +version: 17.1.5 +categories: [release] +--- + +关键错误修复:恢复了与 Android 14–15 的兼容性——在上一个版本中添加 Android 16 支持时意外破坏了它。感谢 [@tbodt](https://mastodon.social/@tbodt) 的修复。 diff --git a/_i18n/cn/_posts/2025-06-18-frida-17-2-0-released.markdown b/_i18n/cn/_posts/2025-06-18-frida-17-2-0-released.markdown new file mode 100644 index 00000000..16b1cf4c --- /dev/null +++ b/_i18n/cn/_posts/2025-06-18-frida-17-2-0-released.markdown @@ -0,0 +1,170 @@ +--- +layout: news_item +title: 'Frida 17.2.0 发布' +date: 2025-06-18 23:35:07 +0200 +author: oleavr +version: 17.2.0 +categories: [release] +--- + +我很高兴宣布 Frida 17.2.0 的发布。此版本专注于使包发现变得极其简单。 + +这就是发现现有 Frida 特定包是多么容易: + +![显示 frida-pm 搜索结果的终端](/img/frida-pm-search.png) + +使用其中任何一个也同样容易: + +![显示 frida-pm 安装结果的终端](/img/frida-pm-install.png) + +**亮点** + +- 🔍 **frida-pm search** – 零噪音结果 (通过 `keywords:frida-gum` 过滤)。 +- 📦 **一键安装** – `frida-pm install ` 即使没有 Node.js 也能工作。 +- 🧩 **编程 API** – 来自 Python、C 等的相同界面。 + +你在这里看到的是 frida-pm CLI 工具,在 frida-tools 14.2.0 中引入。它只有不到 300 行 Python 代码,因为它只是底层 `Frida.PackageManager` 实现的一个薄包装。 + +高级用户和包维护者通常仍会使用 npm/yarn/etc.,但我觉得要求初次使用 Frida 的用户也熟悉庞大的 JavaScript 生态系统可能会让他们感到不知所措和困惑。 + +frida-pm / Frida.PackageManager 的妙处在于搜索只显示 Frida 特定的包。这是通过将 `keywords:frida-gum` 烘焙到搜索查询中来实现的。 + +对于那些维护 Frida 特定包的人,请确保将 `frida-gum` 添加到 package.json 的 `keywords` 字段中。如果你的包是语言/运行时桥接器,请确保也添加 `frida-gum-bridge`。 + +因此,可发现性是这里的关键功能之一。另一个是它可以在没有 Node.js + npm 的系统上运行。虽然我们确实使用 npm 的注册表作为默认后端,但你可以将其指向你喜欢的任何注册表。 + +你还可以通过编程方式访问所有功能。例如,如果你想使用 Python 绑定进行搜索: + +{% highlight py %} +import frida + +pm = frida.PackageManager() +result = pm.search("il2cpp", limit=3) +print(result) +print(result.packages) +{% endhighlight %} + +你会看到类似这样的内容: + +{% highlight bash %} +$ python search.py +PackageSearchResult(packages=[<3 packages>], total=13) +[Package(name="frida-il2cpp-bridge", version="0.12.0", description="A Frida module to dump, trace or hijack any Il2Cpp application at runtime, without needing the global-metadata.dat file.", url="https://npm.im/frida-il2cpp-bridge"), + Package(name="frida-objc-bridge", version="8.0.5", description="Objective-C runtime interop from Frida", url="https://npm.im/frida-objc-bridge"), + Package(name="frida-java-bridge", version="7.0.4", description="Java runtime interop from Frida", url="https://npm.im/frida-java-bridge")] +$ +{% endhighlight %} + +或者也许你想安装几个包: + +{% highlight py %} +import frida + +pm = frida.PackageManager() +result = pm.install(specs=["frida-java-bridge@7.0.4", "frida-il2cpp-bridge"]) +print(result) +print(result.packages) +{% endhighlight %} + +运行时可能看起来像这样: + +{% highlight bash %} +$ python install.py +PackageInstallResult(packages=[<2 packages>]) +[Package(name="frida-java-bridge", version="7.0.4", description="Java runtime interop from Frida"), + Package(name="frida-il2cpp-bridge", version="0.12.0", description="A Frida module to dump, trace or hijack any Il2Cpp application at runtime, without needing the global-metadata.dat file.")] +$ +{% endhighlight %} + +添加安装进度也很容易: + +{% highlight py %} +import frida + +def on_install_progress(phase, fraction, details): + print({ + "phase": phase, + "fraction": fraction, + "details": details, + }) + +pm = frida.PackageManager() +pm.on("install-progress", on_install_progress) +result = pm.install(specs=["frida-java-bridge", "frida-il2cpp-bridge"]) +print(result) +print(result.packages) +{% endhighlight %} + +这可能看起来像这样: + +{% highlight bash %} +$ python install.py +{'phase': 'initializing', 'fraction': 0.0, 'details': None} +{'phase': 'preparing-dependencies', 'fraction': 0.05, 'details': None} +{'phase': 'resolving-package', + 'fraction': -1.0, + 'details': 'frida-java-bridge@latest'} +… +{% endhighlight %} + +既然我们已经看了从 Python 使用 PackageManager API,我可能应该提到从 C 使用此 API 也(几乎)一样容易: + +{% highlight c %} +#include + +int +main (int argc, + char * argv[]) +{ + GCancellable * cancellable = NULL; + GError * error = NULL; + + frida_init (); + + FridaPackageManager * manager = frida_package_manager_new (); + + FridaPackageInstallOptions * opts = frida_package_install_options_new (); + frida_package_install_options_add_spec (opts, "frida-java-bridge@7.0.4"); + frida_package_install_options_add_spec (opts, "frida-il2cpp-bridge"); + + frida_package_manager_install_sync (manager, opts, cancellable, &error); + if (error != NULL) + g_printerr ("%s\n", error->message); + + return (error == NULL) ? 0 : 1; +} +{% endhighlight %} + +如果你想尝试这个例子,请从我们的 [releases][] 获取 frida-core devkit。 + +你可以像这样构建并运行它: + +{% highlight bash %} +$ gcc install.c -o install -I. -L. -lfrida-core -Wl,--gc-sections +$ ./install +{% endhighlight %} + +(frida-core-example.c 的顶部有一个针对 devkit 所针对的特定 OS/arch 定制的示例命令行。) + +请注意,可以通过传递 NULL 省略 `opts`,在这种情况下,如果 package.json 中定义的包尚未安装或版本不匹配,则会安装它们。就像 npm 一样,如果你没有 package.json 文件并直接安装一些包,它会为你创建一个 package.json。 + +此版本还包括其他一些改进和修复: + +- **Compiler**: + - 将 `@frida/net` 升级到 5.0.0。 + - 修复缺少的 shim 资产 (感谢 [@imlihe][])。 + +- **frida-node**: + - 更改 `Device.openChannel()` 的返回类型以公开带有 `destroy()` 的更具体的类型。 + +要升级,请运行: + +{% highlight bash %} +$ pip install --upgrade frida frida-tools +{% endhighlight %} + +享受吧! + + +[releases]: https://github.com/frida/frida/releases +[@imlihe]: https://github.com/imlihe diff --git a/_i18n/cn/_posts/2025-06-19-frida-17-2-1-released.markdown b/_i18n/cn/_posts/2025-06-19-frida-17-2-1-released.markdown new file mode 100644 index 00000000..72a8e1fd --- /dev/null +++ b/_i18n/cn/_posts/2025-06-19-frida-17-2-1-released.markdown @@ -0,0 +1,16 @@ +--- +layout: news_item +title: 'Frida 17.2.1 发布' +date: 2025-06-19 21:03:53 +0200 +author: oleavr +version: 17.2.1 +categories: [release] +--- + +另一个快速的错误修复版本,解决了自上次发布以来发现的几个问题: + +- compiler: 在 Android 上,将后端设为共享库,以避免由于线程局部存储导致的动态链接问题。 +- python: 公开 `PackageManager.registry` 属性。 +- python: 修复了 `Compiler`、`PackageManager` 和 `FileMonitor` 缺少的顶级计数器逻辑,确保信号发射正常工作。 +- python: 为 `PackageManager` 类型添加了 `__repr__` 方法,以便更好地调试。 +- core: 修复了当 libsoup 用作子项目时的构建问题。 diff --git a/_i18n/cn/_posts/2025-06-19-frida-17-2-2-released.markdown b/_i18n/cn/_posts/2025-06-19-frida-17-2-2-released.markdown new file mode 100644 index 00000000..c5dfef96 --- /dev/null +++ b/_i18n/cn/_posts/2025-06-19-frida-17-2-2-released.markdown @@ -0,0 +1,13 @@ +--- +layout: news_item +title: 'Frida 17.2.2 发布' +date: 2025-06-19 23:25:55 +0200 +author: oleavr +version: 17.2.2 +categories: [release] +--- + +我们带着快速的错误修复版本回来了: + +- package-manager: 修复了 lockfile 最新路径。 +- package-manager: 仅报告已安装的包。不再包含未触及的顶级包。还简化了 `install()` 逻辑。 diff --git a/_i18n/cn/_posts/2025-06-20-frida-17-2-3-released.markdown b/_i18n/cn/_posts/2025-06-20-frida-17-2-3-released.markdown new file mode 100644 index 00000000..41151fb0 --- /dev/null +++ b/_i18n/cn/_posts/2025-06-20-frida-17-2-3-released.markdown @@ -0,0 +1,16 @@ +--- +layout: news_item +title: 'Frida 17.2.3 发布' +date: 2025-06-20 01:14:05 +0200 +author: oleavr +version: 17.2.3 +categories: [release] +--- + +专注于改进我们包管理器的快速错误修复版本: + +- package-manager: 修复了作用域规范的处理。 + +- package-manager: 处理带有根条目的 tarball。 + + 此修复确保包含 "package/" 目录条目或根级别任何文件的 tarball 得到正确处理。 diff --git a/_i18n/cn/_posts/2025-06-20-frida-17-2-4-released.markdown b/_i18n/cn/_posts/2025-06-20-frida-17-2-4-released.markdown new file mode 100644 index 00000000..e3179960 --- /dev/null +++ b/_i18n/cn/_posts/2025-06-20-frida-17-2-4-released.markdown @@ -0,0 +1,16 @@ +--- +layout: news_item +title: 'Frida 17.2.4 发布' +date: 2025-06-20 15:45:42 +0200 +author: oleavr +version: 17.2.4 +categories: [release] +--- + +另一个快速的错误修复版本,旨在改进我们的包管理器,[@hsorbo][] 和我一直在努力工作。以下是新内容: + +- package-manager: 修复依赖安装死锁。当一个包的子依赖也是安装堆栈中更高层另一个包的依赖时,可能会发生死锁。子依赖会等待更高层的包被物理安装,但那个包在解决其子依赖之前不会完成自己的安装,从而造成循环等待。 +- package-manager: 改进清单处理,使我们更接近 npm 的行为。(与 [@hsorbo][] 共同编写。) +- package-manager: 改进进度报告。 + +[@hsorbo]: https://twitter.com/hsorbo diff --git a/_i18n/cn/_posts/2025-06-23-frida-17-2-5-released.markdown b/_i18n/cn/_posts/2025-06-23-frida-17-2-5-released.markdown new file mode 100644 index 00000000..bfe41577 --- /dev/null +++ b/_i18n/cn/_posts/2025-06-23-frida-17-2-5-released.markdown @@ -0,0 +1,25 @@ +--- +layout: news_item +title: 'Frida 17.2.5 发布' +date: 2025-06-23 18:44:36 +0200 +author: oleavr +version: 17.2.5 +categories: [release] +--- + +此版本为 Frida 带来了重要的修复和改进。以下是亮点: + +- frida-node: 保持 TSFN 存活直到 promise 解决,防止可能导致 Node.js 提前退出并显示“检测到未解决的顶级 await”警告的竞争条件。感谢 [@mrmacete][] 和 [@hsorbo][] 帮助追踪此问题。 + +- frida-node: 简化 `findMatchingDevice()` (与 [@hsorbo][] 共同编写)。 + +- package-manager: 仅在明确请求时才升级。 + +- package-manager: 修复 `dev` 逻辑。 + +- docs: 修复 README 中的 Mapper URL (感谢 [@cmdlinescan][])。 + + +[@mrmacete]: https://twitter.com/bezjaje +[@hsorbo]: https://twitter.com/hsorbo +[@cmdlinescan]: https://github.com/cmdlinescan diff --git a/_i18n/cn/_posts/2025-06-27-frida-17-2-6-released.markdown b/_i18n/cn/_posts/2025-06-27-frida-17-2-6-released.markdown new file mode 100644 index 00000000..a65d5ce1 --- /dev/null +++ b/_i18n/cn/_posts/2025-06-27-frida-17-2-6-released.markdown @@ -0,0 +1,27 @@ +--- +layout: news_item +title: 'Frida 17.2.6 发布' +date: 2025-06-27 16:00:01 +0200 +author: oleavr +version: 17.2.6 +categories: [release] +--- + +我们很高兴宣布 Frida 17.2.6,包含两个重要修复: + +- **buffer**: 修复 `read_fixed_string()` 中的 `max_length`。 + + `max_length` 现在被正确地限制在请求的大小和缓冲区的大小之内。 + + 感谢 [@mrmacete][]! + +- **agent**: 在模拟领域禁用 Exceptor。 + + Exceptor 需要 hook `signal()` 和 `sigaction()`,但它们在 libc 中。 + 这导致 `gum_mprotect()` 中止,因为它无法更改 libc 的只读映射。此修复防止了在 Android 14 和 15 AVD 上使用 `frida-server` 或 `frida-inject` 时观察到的崩溃。 + + 感谢 [@ptrstr][]! + + +[@mrmacete]: https://twitter.com/bezjaje +[@ptrstr]: https://github.com/ptrstr diff --git a/_i18n/cn/_posts/2025-07-01-frida-17-2-7-released.markdown b/_i18n/cn/_posts/2025-07-01-frida-17-2-7-released.markdown new file mode 100644 index 00000000..657680cd --- /dev/null +++ b/_i18n/cn/_posts/2025-07-01-frida-17-2-7-released.markdown @@ -0,0 +1,22 @@ +--- +layout: news_item +title: 'Frida 17.2.7 发布' +date: 2025-07-01 23:40:14 +0200 +author: oleavr +version: 17.2.7 +categories: [release] +--- + +我们很高兴宣布 Frida 17.2.7 的发布,其特点是对我们的包管理器进行了重大改进。 + +- **package-manager**: 改进解析和提升以更紧密地模仿 npm 的行为。与 [@hsorbo][] 共同编写。感谢您的帮助! +- **package-manager**: 为 `role` 添加 `install()` 选项,实现相当于 npm install 的 `--save-*` 开关。 +- **package-manager**: 为 `omits` 添加 `install()` 选项,以实现相当于 npm install 的 `--omit=x` 开关。 +- **package-manager**: 改进对可选包的处理。 +- **package-manager**: 在非 Windows 系统上提取时处理文件模式。 +- **package-manager**: 修复 `has_install_script` 逻辑以同时考虑 `preinstall` 和 `postinstall` 脚本。 +- **meson**: 澄清 Vala 构建系统说明。Vala README 仅提到 autotools 说明,当以这种方式编译 Vala 时,`-frida` 后缀不会添加到版本字符串中,导致 frida-core 对 Vala 的检查失败。我们现在澄清 Vala 需要使用 Meson 从源代码构建。感谢 [@grimler][] 指出这一点! + + +[@hsorbo]: https://twitter.com/hsorbo +[@grimler]: https://mastodon.social/@grimler diff --git a/_i18n/cn/_posts/2025-07-02-frida-17-2-8-released.markdown b/_i18n/cn/_posts/2025-07-02-frida-17-2-8-released.markdown new file mode 100644 index 00000000..6b1957d2 --- /dev/null +++ b/_i18n/cn/_posts/2025-07-02-frida-17-2-8-released.markdown @@ -0,0 +1,12 @@ +--- +layout: news_item +title: 'Frida 17.2.8 发布' +date: 2025-07-02 00:09:26 +0200 +author: oleavr +version: 17.2.8 +categories: [release] +--- + +快速的错误修复版本,解决影响我们 Windows 用户的问题: + +- **package-manager**: 修复 Windows 上损坏的 `#if`。不正确的预处理器指令导致在 Windows 平台上编译时构建失败。 diff --git a/_i18n/cn/_posts/2025-07-02-frida-17-2-9-released.markdown b/_i18n/cn/_posts/2025-07-02-frida-17-2-9-released.markdown new file mode 100644 index 00000000..a3de43d3 --- /dev/null +++ b/_i18n/cn/_posts/2025-07-02-frida-17-2-9-released.markdown @@ -0,0 +1,18 @@ +--- +layout: news_item +title: 'Frida 17.2.9 发布' +date: 2025-07-02 12:59:19 +0200 +author: oleavr +version: 17.2.9 +categories: [release] +--- + +这次我们为您带来初步的 iOS 26 支持,以及针对我们的 Node.js 绑定的错误修复: + +- **fruity**: 添加了对在强制执行调试器映射 (iOS 26) 且我们无法从目标进程内部将内存保护翻转回可执行文件的 iOS 目标上注入 gadget 的支持。在这种情况下,gadget 配置将把 `code_signing` 设置为 `required`,直到 Interceptor 支持强制执行的调试器映射。感谢 [@mrmacete][]! + +- **device**: 修复了 `stdio` 选项未通过 `spawn()` 传递的问题,导致子进程始终继承 stdio。由 [@hsorbo][] 共同编写。 + + +[@mrmacete]: https://twitter.com/bezjaje +[@hsorbo]: https://twitter.com/hsorbo diff --git a/_i18n/cn/_posts/2025-07-03-frida-17-2-10-released.markdown b/_i18n/cn/_posts/2025-07-03-frida-17-2-10-released.markdown new file mode 100644 index 00000000..81ca6818 --- /dev/null +++ b/_i18n/cn/_posts/2025-07-03-frida-17-2-10-released.markdown @@ -0,0 +1,19 @@ +--- +layout: news_item +title: 'Frida 17.2.10 发布' +date: 2025-07-03 12:57:12 +0200 +author: oleavr +version: 17.2.10 +categories: [release] +--- + +快速的错误修复版本,解决了两个重要问题: + +- **frida-node**: 修复 keep-alive ThreadSafeFunction 拆卸。在 keep-alive 场景中使用 `napi_release_threadsafe_function()`,以便在丢弃 libuv 句柄之前有时间运行挂起的微任务。这可以防止拆卸期间出现“未解决的顶级 await”。由 [@as0ler][]、[@hsorbo][] 和 [@mrmacete][] 共同编写。 + +- **barebone**: 确保 RustModule C ABI 入口点在垃圾回收中存活。较新的 Rust 工具链使用 `--gc-sections`,这会剥离未使用的部分。我们重构了 `make_linker_script()` 以扫描 Rust 源代码中旨在可见的符号,并发出 `KEEP(*(.text.))` 指令,以便保留这些入口点。 + + +[@as0ler]: https://x.com/as0ler +[@hsorbo]: https://x.com/hsorbo +[@mrmacete]: https://x.com/bezjaje diff --git a/_i18n/cn/_posts/2025-07-03-frida-17-2-11-released.markdown b/_i18n/cn/_posts/2025-07-03-frida-17-2-11-released.markdown new file mode 100644 index 00000000..bc114a89 --- /dev/null +++ b/_i18n/cn/_posts/2025-07-03-frida-17-2-11-released.markdown @@ -0,0 +1,19 @@ +--- +layout: news_item +title: 'Frida 17.2.11 发布' +date: 2025-07-03 16:10:25 +0200 +author: oleavr +version: 17.2.11 +categories: [release] +--- + +就在我们以为我们已经搞定一切的时候,软件提醒我们它有多难!感谢 [@mrmacete][]、[@hsorbo][] 和 [@0xmurphy][],我们迅速解决了问题并为您提供以下修复: + +- frida-node: 修复 `fdn_keep_alive_until()` TSFN 生命周期问题。我们将 TSFN 设置为 `NULL`,这导致当其他引用持有者尝试安排清理时崩溃。 + +- fruity: 修复杀死预热目标时的问题。在 spawn 时杀死预热进程会导致“连接关闭”错误。此更改捕获错误,以便可以 spawn 一个新实例。 + + +[@as0ler]: https://x.com/as0ler +[@hsorbo]: https://x.com/hsorbo +[@mrmacete]: https://x.com/bezjaje diff --git a/_i18n/cn/_posts/2025-07-18-frida-17-2-12-released.markdown b/_i18n/cn/_posts/2025-07-18-frida-17-2-12-released.markdown new file mode 100644 index 00000000..a1aadd4e --- /dev/null +++ b/_i18n/cn/_posts/2025-07-18-frida-17-2-12-released.markdown @@ -0,0 +1,46 @@ +--- +layout: news_item +title: 'Frida 17.2.12 发布' +date: 2025-07-18 10:32:31 +0200 +author: oleavr +version: 17.2.12 +categories: [release] +--- + +我们很高兴宣布 Frida 17.2.12,它带来了在没有操作系统的情况下运行 Gum 的初步支持,对我们的 Barebone 后端的重大增强,以及各种改进和修复。 + +- android: 升级了 `system-server` 中的 `frida-java-bridge` 以包含对不正确的 ART 类规范偏移检测的修复。这防止了由于 `libart.so` 独立于 SDK 版本更新而导致的崩溃,这以前会导致偏移不匹配。该修复现在使用通过已知类的运行时检测而不是 SDK 启发式方法,提高了跨 Android 更新的可靠性。 + + **非常感谢 [@AeonLucid][] 带头开展这项工作 —— 这是一项真正的英雄努力,面对不断变化的 ART 内部结构,为 Frida 的 Android 支持带来了坚如磐石的可靠性。** + +- gum: 添加了对在没有操作系统的情况下运行的初步支持,使 Frida 能够在裸机目标上运行。集成商用特定于目标/固件的符号覆盖所需的弱符号。我们有一个正在进行的 XNU 代理,让我们可以 Apple 的 OS 内核中运行 JavaScript。 + +- barebone: 在 arm64 后端添加了对 APRR 的支持(感谢结对编程 [@hsorbo][])。 + +- barebone: 添加了对 R_AARCH64_PREL32 重定位的支持(感谢 [@hsorbo][]),提高了与更多 ARM64 二进制文件的兼容性。 + +- barebone: 实现了 `Memory.protect()`,提供了在裸机目标上更改内存保护的能力。 + +- fruity: 在上传之前喷射 Gadget 的 `r-x` 页面以将其转换为调试器映射(感谢 [@hsorbo][]、[@mrmacete][] 和 [@as0ler][]),修复了较新硬件代上的问题。 + +- buffer: 添加了新方法:`read_bytes()`、`write_bytes()`、`write_int64()`、`write_uint32()`、`write_int32()`、`write_uint16()`、`write_int16()`、`write_uint8()`、`write_int8()`,扩展了 Buffer 处理各种数据类型的 API。 + +- gumjs: 避免用于内联源映射解析的正则表达式以减少堆栈使用,提高了在堆栈大小受限的平台上的稳定性。 + +- build: 添加了对 'armv6kz-' 前缀工具链的支持(感谢 [@zetierhg][]),提高了与更多工具链的兼容性。 + +- build: 修复了 Python < 3.9 的类型定义(感谢 [@oriori1703][]),确保与旧 Python 版本的兼容性。 + +- build: 为 FreeBSD 链接器特殊处理 `ld` 脚本以修复构建问题(感谢 [@grimler][]),改进了 FreeBSD 支持。 + +- devkit: 使符号前缀可选(感谢 [@Hexploitable][]),允许消费者选择是否为第三方符号添加前缀以避免冲突。 + + +[@AeonLucid]: https://x.com/AeonLucid +[@hsorbo]: https://x.com/hsorbo +[@mrmacete]: https://x.com/bezjaje +[@as0ler]: https://x.com/as0ler +[@zetierhg]: https://github.com/zetier-hg +[@oriori1703]: https://github.com/oriori1703 +[@grimler]: https://mastodon.social/@grimler +[@Hexploitable]: https://x.com/Hexploitable diff --git a/_i18n/cn/_posts/2025-07-21-frida-17-2-13-released.markdown b/_i18n/cn/_posts/2025-07-21-frida-17-2-13-released.markdown new file mode 100644 index 00000000..dec272eb --- /dev/null +++ b/_i18n/cn/_posts/2025-07-21-frida-17-2-13-released.markdown @@ -0,0 +1,16 @@ +--- +layout: news_item +title: 'Frida 17.2.13 发布' +date: 2025-07-21 15:06:29 +0200 +author: oleavr +version: 17.2.13 +categories: [release] +--- + +这是一个快速的错误修复版本: + +- android: 升级 system-server 中的 frida-java-bridge 以获取 [@AeonLucid][] 的 ART 偏移检测修复。 +- barebone: 支持实现 DebugSymbol API。 + + +[@AeonLucid]: https://x.com/AeonLucid diff --git a/_i18n/cn/_posts/2025-07-24-frida-17-2-14-released.markdown b/_i18n/cn/_posts/2025-07-24-frida-17-2-14-released.markdown new file mode 100644 index 00000000..c1a41cc5 --- /dev/null +++ b/_i18n/cn/_posts/2025-07-24-frida-17-2-14-released.markdown @@ -0,0 +1,37 @@ +--- +layout: news_item +title: 'Frida 17.2.14 发布' +date: 2025-07-24 11:16:55 +0200 +author: oleavr +version: 17.2.14 +categories: [release] +--- + +此版本带来了对 Cloak API 和模块处理的若干改进,以及一些必要的更新和错误修复。特别感谢 [@AeonLucid][] 为 Android 支持做出的贡献。 + +- **android:** 更新了 `system-server` 中的 `frida-java-bridge` 以包含 [@AeonLucid][] 改进的 ART 偏移查找。详见 [frida-java-bridge#362][]。 + +- **cloak:** 在 Android 上添加了对 `Art::GetOsThreadStat` 的支持,解决了类似于 [frida-core#500][] 的问题,即 Zygote 在继续之前等待进程变为单线程,否则会崩溃。此更改适应了 `art::GetOsThreadStat` 的新用法。(感谢 [@AeonLucid][]) + +- **cloak:** 堵住了 `ThreadCountCloaker.dispose()` 中的内存泄漏,我们未能链接到 `GObject.dispose()`。 + +- **module:** 添加了一个可选的 `get_version()` 虚拟函数。 + +- **module:** 使大多数接口方法可选,以减少 Barebone 集成所需的样板代码量。 + +- **darwin:** 实现了 `Module.get_version()`,在可用时公开 `LC_SOURCE_VERSION`。 + +- **darwin-module:** 添加了 `source-version` 属性以公开 `LC_SOURCE_VERSION`(如果存在)。 + +- **darwin-module:** 添加了对非 Darwin 内存中使用的支持,例如在 XNU 内部。 + +- **gumjs:** 向 JavaScript 公开了 `Module#version`。 + +- **barebone:** 添加了对注册模块的支持。 + +- **compiler:** 将 `@types/frida-gum` 升级到 19.0.1。 + + +[@AeonLucid]: https://x.com/AeonLucid +[frida-java-bridge#362]: https://github.com/frida/frida-java-bridge/pull/362 +[frida-core#500]: https://github.com/frida/frida-core/issues/500 diff --git a/_i18n/cn/_posts/2025-08-02-frida-17-2-15-released.markdown b/_i18n/cn/_posts/2025-08-02-frida-17-2-15-released.markdown new file mode 100644 index 00000000..a4935385 --- /dev/null +++ b/_i18n/cn/_posts/2025-08-02-frida-17-2-15-released.markdown @@ -0,0 +1,27 @@ +--- +layout: news_item +title: 'Frida 17.2.15 发布' +date: 2025-08-02 09:42:58 +0200 +author: oleavr +version: 17.2.15 +categories: [release] +--- + +这个版本已经酝酿了一段时间,一大堆好东西刚刚落地: + +- darwin / fruity: 通过双重映射支持 iOS 26,并在 palera1n 上启用注入。非常感谢 [@mrmacete][] 推动这项工作。 +- android: 升级 system-server 中的 frida-java-bridge 以获取 [@AeonLucid][] 的 ART 偏移查找改进。 +- platform: 添加初步的 visionOS 支持。非常感谢 [@demonguy][] 开拓这个全新的领域。 +- freebsd: 将 x86 (32-bit) 带入派对。感谢 [@saruman9][] 的实现。 +- threads: 在 Linux 和 Darwin 的 ARM/arm64 上连接 NEON 寄存器访问 – 感谢 [@londek][] 的补丁集。 +- barebone: 在 NativeFunction / NativeCallback 中添加对 uint 类型的支持,并优雅地处理 MMU 被禁用的情况。 +- buffer: 修复 BufferReader.read_pointer(),我们错误地期望传入偏移量。 + +享受吧! + + +[@mrmacete]: https://x.com/bezjaje +[@AeonLucid]: https://x.com/AeonLucid +[@demonguy]: https://github.com/demonguy +[@saruman9]: https://github.com/saruman9 +[@londek]: https://github.com/londek diff --git a/_i18n/cn/_posts/2025-08-12-frida-17-2-16-released.markdown b/_i18n/cn/_posts/2025-08-12-frida-17-2-16-released.markdown new file mode 100644 index 00000000..1f686177 --- /dev/null +++ b/_i18n/cn/_posts/2025-08-12-frida-17-2-16-released.markdown @@ -0,0 +1,19 @@ +--- +layout: news_item +title: 'Frida 17.2.16 发布' +date: 2025-08-12 10:15:55 +0200 +author: oleavr +version: 17.2.16 +categories: [release] +--- + +新鲜的维护版本,带来了一系列修复和生活质量改进: + +- gumjs: 确保仅在其匹配的 `onEnter` 实际运行后才调用 `onLeave`,消除了在调用中途附加 hook 时的不可预测行为。感谢 [@mrmacete][]! +- darwin-module: 交换 hook dyld “所有图像信息” 中的 `lldb_image_notifier` 指针,以保持模块注册表即使在通知程序只有一条指令长的现代 iOS 设备上也能正常工作。干杯 [@mrmacete][]! +- barebone: 添加 `try_remap_writable_pages()`。 +- barebone: 允许覆盖 `query_rwx_support()` 以针对奇异平台定制 RWX 能力探测。 +- frida-node: 在 TypeScript 阶段遵守 `package-lock.json`,以保持 JS 依赖树完全确定性。 + + +[@mrmacete]: https://x.com/bezjaje diff --git a/_i18n/cn/_posts/2025-08-20-frida-17-2-17-released.markdown b/_i18n/cn/_posts/2025-08-20-frida-17-2-17-released.markdown new file mode 100644 index 00000000..7d7d50b9 --- /dev/null +++ b/_i18n/cn/_posts/2025-08-20-frida-17-2-17-released.markdown @@ -0,0 +1,15 @@ +--- +layout: news_item +title: 'Frida 17.2.17 发布' +date: 2025-08-20 12:07:29 +0200 +author: oleavr +version: 17.2.17 +categories: [release] +--- + +微小的发布,巨大的影响:来自 [@mrmacete][] 的这两个单行修复确保了 Interceptor 在较新的 iOS 版本上表现正确。 + +- code-allocator: 使用 `pc` 进行 slice 地址检查,以便即使 `data` 和 `pc` 不同(例如在 iOS 26 上),`is_near` 和 `is_aligned` 也能保持同步。感谢 [@mrmacete][]! + + +[@mrmacete]: https://x.com/bezjaje diff --git a/_i18n/cn/_posts/2025-09-15-frida-17-3-0-released.markdown b/_i18n/cn/_posts/2025-09-15-frida-17-3-0-released.markdown new file mode 100644 index 00000000..2a6a6e6a --- /dev/null +++ b/_i18n/cn/_posts/2025-09-15-frida-17-3-0-released.markdown @@ -0,0 +1,23 @@ +--- +layout: news_item +title: 'Frida 17.3.0 发布' +date: 2025-09-15 22:57:13 +0200 +author: oleavr +version: 17.3.0 +categories: [release] +--- + +新鲜的豆子,新功能!此版本为我们的 Barebone 和 Fruity 后端带来了令人兴奋的功能,并消除了一些粗糙的边缘: + +- barebone: 添加对 XNU 注入的基本支持,已在 QEMU 中的 iOS 14.0 上成功测试。与 [@hsorbo][] 共同编写。 +- barebone: 公开带下划线前缀的 CModule 符号,以便它们可从 Frida 脚本中使用。 +- fruity: 每当隧道超时或遇到其他传输错误时,回退到 usbmux。感谢 [@Xplo8E][] 的推动。 +- fruity: 处理 CoreDevice 配对事件,并使用 FIFO 而不是序列号匹配配对请求和响应。非常感谢 [@hsorbo][]。 +- fruity: 处理拆卸过程中的一些边缘情况。感谢 [@mrmacete][]。 + +享受吧! + + +[@hsorbo]: https://x.com/hsorbo +[@Xplo8E]: https://github.com/Xplo8E +[@mrmacete]: https://github.com/mrmacete diff --git a/_i18n/cn/_posts/2025-09-18-frida-17-3-1-released.markdown b/_i18n/cn/_posts/2025-09-18-frida-17-3-1-released.markdown new file mode 100644 index 00000000..b03532e1 --- /dev/null +++ b/_i18n/cn/_posts/2025-09-18-frida-17-3-1-released.markdown @@ -0,0 +1,10 @@ +--- +layout: news_item +title: 'Frida 17.3.1 发布' +date: 2025-09-18 07:39:51 +0200 +author: oleavr +version: 17.3.1 +categories: [release] +--- + +刚出炉的维护版本,带来了大幅改进的 Fruity CoreDevice 隧道性能。 diff --git a/_i18n/cn/_posts/2025-09-19-frida-17-3-2-released.markdown b/_i18n/cn/_posts/2025-09-19-frida-17-3-2-released.markdown new file mode 100644 index 00000000..707a12f0 --- /dev/null +++ b/_i18n/cn/_posts/2025-09-19-frida-17-3-2-released.markdown @@ -0,0 +1,16 @@ +--- +layout: news_item +title: 'Frida 17.3.2 发布' +date: 2025-09-19 17:16:32 +0200 +author: oleavr +version: 17.3.2 +categories: [release] +--- + +新鲜的比特准备好了!此版本专注于从我们的 Fruity 后端榨取更多性能: + +- fruity: 批量数据报传递,使跨线程切换更具确定性并减少上下文切换开销。 +- ncm: 重做 host→device 调度。我们现在保留一个 OUT 传输的滚动窗口,并在任何 URB 完成后立即重新填充,保持批量管道繁忙,并在我们的测试中将 HS 吞吐量从 ~29 MB/s 提高到 ~34 MB/s。 +- ncm: 切换到固定槽 NDP 布局,将 O(k²) “收缩直到适合” 打包器变为 O(k)。在 256 MiB 传输中,这会将布局时间从 ~1.1 秒降低到本底噪声。 + +享受吧,让我们知道它对您的效果如何! diff --git a/_i18n/cn/_posts/2025-10-12-frida-17-4-0-released.markdown b/_i18n/cn/_posts/2025-10-12-frida-17-4-0-released.markdown new file mode 100644 index 00000000..99cbf428 --- /dev/null +++ b/_i18n/cn/_posts/2025-10-12-frida-17-4-0-released.markdown @@ -0,0 +1,36 @@ +--- +layout: news_item +title: 'Frida 17.4.0 发布' +date: 2025-10-12 08:35:50 +0200 +author: oleavr +version: 17.4.0 +categories: [release] +--- + +又到了功能丰富的发布时间!亮点: + +- simmy: 全新的后端,通过 CoreSimulator.framework 与 Apple 模拟器对话。Spawn 应用、插桩进程,通常像对待任何其他设备一样对待模拟器 —— 所有这些都可以在 Frida 中舒适地完成。 + +- darwin: 支持 `dyld_sim` 的早期插桩,因此您甚至可以在动态加载程序完成模拟进程的引导之前附加并加载脚本。 + +- darwin: 修复最新 iOS 18 模拟器上的 sysroot 检测,其中 `dyld_sim` 现在对 `_dyld_image_count` 和 `TASK_DYLD_ALL_IMAGE_INFO_64` 隐藏。感谢 [@CodeColorist][] 追踪到这个问题! + +- fruity: 每当我们遇到 `InvalidHostID` 错误时自动取消配对,允许下一次配对尝试成功。[@mrmacete][] 的出色工作! + +- android: 将 `system-server` 更新为 `frida-java-bridge` 7.0.9。更改: + - 修复 Android 16 上的 Java.deoptimize\*() 和 Java.backtrace()。感谢 [@hsorbo][]! + - 改进类型定义。感谢 [@yotamN][]! + +- host-session: 添加跨后端通信,因此通过一个后端发现的设备现在可用于另一个后端的内部。 + +- base: 引入 `StdioPipes` 和 `FileDescriptor` 助手,由 Darwin、Linux 和 FreeBSD 后端共享。 + +- value: 新的 `VariantReader.list_members()` 助手,便于内省。 + +享受吧! + + +[@CodeColorist]: https://x.com/CodeColorist +[@mrmacete]: https://x.com/bezjaje +[@hsorbo]: https://x.com/hsorbo +[@yotamN]: https://github.com/yotamN diff --git a/_i18n/cn/_posts/2025-10-24-frida-17-4-1-released.markdown b/_i18n/cn/_posts/2025-10-24-frida-17-4-1-released.markdown new file mode 100644 index 00000000..2b52516d --- /dev/null +++ b/_i18n/cn/_posts/2025-10-24-frida-17-4-1-released.markdown @@ -0,0 +1,23 @@ +--- +layout: news_item +title: 'Frida 17.4.1 发布' +date: 2025-10-24 15:25:28 +0200 +author: oleavr +version: 17.4.1 +categories: [release] +--- + +一个小而美味的后续版本,以保持势头: + +- android: 升级 `system-server` 中的 `frida-java-bridge`,带来最新的稳定性改进: + - android: 处理静态 trampoline 修复,因此我们将每个 ArtMethod 回滚到其先前的入口点,以避免 hook 被绕过。 + - android: 在 GC 后同步 ArtMethod 类字段,以便我们的替换 ArtMethod 实例不会过时,并导致未定义的行为。 + 感谢结对编程,[@hsorbo][]。 +- devkit-assets: 将 GumJS 示例升级到新的 Frida 17 GumJS API。感谢 [@Hexploitable][] 促成此事。 +- freebsd: 连接 PTY 支持,因此依赖于控制终端的 spawn/attach 操作现在开箱即用。 + +享受吧! + + +[@hsorbo]: https://x.com/hsorbo +[@Hexploitable]: https://x.com/Hexploitable diff --git a/_i18n/cn/_posts/2025-10-29-frida-17-4-2-released.markdown b/_i18n/cn/_posts/2025-10-29-frida-17-4-2-released.markdown new file mode 100644 index 00000000..2f77decf --- /dev/null +++ b/_i18n/cn/_posts/2025-10-29-frida-17-4-2-released.markdown @@ -0,0 +1,19 @@ +--- +layout: news_item +title: 'Frida 17.4.2 发布' +date: 2025-10-29 16:48:10 +0100 +author: oleavr +version: 17.4.2 +categories: [release] +--- + +本周是 Simmy 后端改进的一批更新。感谢 [@hsorbo][] 的结对编程,带来了以下改进: + +- simmy: 实现 `get_frontmost_application()`,使得弄清楚当前谁在聚光灯下变得轻而易举。 +- simmy: 修复 `query_system_parameters()` 中报告的 `hardware.product`,例如返回 `iPhone18,2` 而不是 “iPhone 17 Pro Max”。 +- simmy: 连接应用程序图标。 + +享受吧! + + +[@hsorbo]: https://x.com/hsorbo diff --git a/_i18n/cn/_posts/2025-10-31-frida-17-4-3-released.markdown b/_i18n/cn/_posts/2025-10-31-frida-17-4-3-released.markdown new file mode 100644 index 00000000..b4da1b39 --- /dev/null +++ b/_i18n/cn/_posts/2025-10-31-frida-17-4-3-released.markdown @@ -0,0 +1,22 @@ +--- +layout: news_item +title: 'Frida 17.4.3 发布' +date: 2025-10-31 20:24:19 +0100 +author: oleavr +version: 17.4.3 +categories: [release] +--- + +万圣节带来了一小批修复和改进: + +- simmy: 当启用系统完整性保护 (SIP) 时,优雅地降级依赖于注入 SpringBoard 的功能。这意味着 `get_frontmost_application()` 和图标检索现在会回退而不是崩溃。 + +- simmy: 修复了当请求 bundle ID 子集时已安装应用的枚举问题。感谢 [@hsorbo][] 的协助! + +- docs: 更新 README 的 Apple 证书部分以反映当前的现实。感谢 [@gemesa][] 发现并修复了过时的部分! + +享受吧,祝黑客愉快! + + +[@hsorbo]: https://x.com/hsorbo +[@gemesa]: https://github.com/gemesa diff --git a/_i18n/cn/_posts/2025-11-01-frida-17-4-4-released.markdown b/_i18n/cn/_posts/2025-11-01-frida-17-4-4-released.markdown new file mode 100644 index 00000000..0aa46deb --- /dev/null +++ b/_i18n/cn/_posts/2025-11-01-frida-17-4-4-released.markdown @@ -0,0 +1,15 @@ +--- +layout: news_item +title: 'Frida 17.4.4 发布' +date: 2025-11-01 00:38:24 +0100 +author: oleavr +version: 17.4.4 +categories: [release] +--- + +针对 Darwin 用户的小而重要的更新: + +- **darwin**: 恢复在 rootful 系统上运行 iOS ≥ 16 时的应用列表/启动功能。这使得 frida-core 提交 dccb612 起死回生,此前 8108d4d 在修复两个 Interceptor 单例泄漏时破坏了它。事实证明,springboard.m 中的泄漏是有意的,作为一次性初始化逻辑,不需要拆卸;没有它,我们应用的插桩会立即被还原。感谢 [@alexhude][] 的提醒。 + + +[@alexhude]: https://github.com/alexhude diff --git a/_i18n/cn/_posts/2025-11-04-frida-17-5-0-released.markdown b/_i18n/cn/_posts/2025-11-04-frida-17-5-0-released.markdown new file mode 100644 index 00000000..0aec8889 --- /dev/null +++ b/_i18n/cn/_posts/2025-11-04-frida-17-5-0-released.markdown @@ -0,0 +1,143 @@ +--- +layout: news_item +title: 'Frida 17.5.0 发布' +date: 2025-11-04 14:02:32 +0100 +author: oleavr +version: 17.5.0 +categories: [release] +--- + +喝了几杯 ☕ 并提交了一堆代码后,我们带着功能丰富的版本回来了。亮点包括更智能的编译器、更稳固的 Darwin 内部结构以及大规模的 Swift 改革 —— 绑定现在是 async/await 优先、无委托且很大程度上与平台无关的。 + +### 亮点 + +- **compiler**: 向 `CompilerOptions` 添加了 `platform` 和 `externals` 选项,并将它们一直传递到 Go 后端。 + 这让 **frida-compile** (和 `Frida.Compiler`) 可以根据你的目标平台定制输出,并将选定的模块视为外部模块 —— 例如,在为 GumJS 代理构建插件时,代理公开的 API 应该在运行时链接而不是打包在一起。 + (感谢 [@leonitousconforti][]) +- **darwin**: 重写了 `query_shared_cache_range()` 以解析 dyld 共享缓存头,而不是从 `AllImageInfos` 中找到的基地址遍历 VM 区域。这消除了猜测,即使页面被写时复制也能确保范围正确。 + (感谢结对编程,[@hsorbo][]) +- **darwin**: `AllImageInfos` 现在报告 Dyld 共享缓存 UUID 和 slide。 + (感谢结对编程,[@hsorbo][]) +- **simmy**: `spawn()` 增加了正确的 `argv` 和 `env` 连接,因此模拟器现在的行为更像真实设备。 +- **frida-node**: 生成的 `from_value()` 助手现在包含继承的属性,因此像 `externals` 这样的选项可以正确传播。 +- **frida-python**: 修复了 `PackageManager` 规范选项解析中一个微小但会导致泄漏的角落情况。 + +### Swift 绑定:现代、跨平台的改造 🍎 + +**Frida Swift 绑定** 已经被广泛重构,以符合 Swift 惯用语、并发优先且跨平台。 + +- **到处都是 Async/await** — 大多数 API 现在使用 Swift Concurrency 并支持通过 `GCancellable` 进行 `Task` 取消。 +- **移除委托** — 基于委托的回调已被 **异步事件流** (`AsyncStream`) 取代,使事件处理符合人体工程学且可组合。 +- **线程友好** — `api: Support invocation from any thread` 允许从非主线程安全调用。(感谢结对编程,[@hsorbo][]。) +- **纯 Swift 核心 + 跨平台** — 核心绑定现在不依赖 Foundation 和 Dispatch,并使用纯 Swift 类型 (二进制数据表示为 `[UInt8]`)。目前还有两个小缺口:Marshal 助手中的 JSON 编码/解码目前使用 Foundation;稍后将添加非 Foundation 回退。 +- **SwiftUI 友好** — 新的 `DeviceListModel`,一个 `@MainActor ObservableObject`,公开 `@Published devices` 和 `discoveryState` 以实现流畅的 SwiftUI 集成。 +- **图标可移植性** — 平台特定的图像处理已被可移植的 `Icon` 枚举取代,带有用于 `CGImage`、`NSImage`、`UIImage` 和 `SwiftUI.Image` 的平台适配器。 +- **API 稳定性改进** — 公共枚举用 `@frozen` 注释,一些复杂的引用类型在必要时标记为 `@unchecked Sendable`。 + +> 注意:此版本不包含 `frida-swift` 预构建二进制文件;如果你使用 Swift 绑定,你应该 `git clone` 并从 `main` 构建以获取最新更改。 +> +> 另外请注意,Swift 绑定仍然是 **实验性的和不断发展的** — 虽然新 API 是一个巨大的飞跃,但在即将发布的版本中 Swift 层稳定之前,它们可能会继续更改。 + +### 快速示例 (来自 frida-swift README) + +`DeviceListModel` (UI 友好模型): + +```swift +import Combine + +@MainActor +public final class DeviceListModel: ObservableObject { + @Published public private(set) var devices: [Device] = [] + @Published public private(set) var discoveryState: DiscoveryState = .discovering + + @frozen + public enum DiscoveryState: Equatable { + case discovering + case ready + } + + public let manager: DeviceManager + + public init(manager: DeviceManager) { … } +} +``` + +现在可以像这样使用: + +```swift +import Frida +import SwiftUI + +struct DevicesView: View { + @StateObject private var model = DeviceListModel(manager: DeviceManager()) + @State private var selectedDevice: Device? + @State private var session: Session? + + var body: some View { + NavigationStack { + List(model.devices, id: \.id) { device in + Button { + Task { + selectedDevice = device + session = try? await device.attach(to: 12345) + } + } label: { + VStack(alignment: .leading) { + Text(device.name) + .font(.headline) + Text(device.kind.rawValue) + .font(.subheadline) + .foregroundStyle(.secondary) + } + } + } + .navigationTitle("Frida Devices") + .overlay { + if model.devices.isEmpty { + ProgressView("Searching for devices…") + } + } + } + } +} +``` + +完整的脚本生命周期示例: + +```swift +func testFullCycle() async throws { + let manager = DeviceManager() + + for await devices in await manager.snapshots() { + guard let local = devices.first(where: { $0.kind == .local }) else { + continue + } + + let session = try await local.attach(to: 12345) + let script = try await session.createScript(""" + console.log("hello"); + send(1337); + """) + + Task { + for await event in script.events { + switch event { + case .message(let message, _): + print("Message:", message) + case .destroyed: + print("Script destroyed") + } + } + } + + try await script.load() + break + } +} +``` + +享受吧,一如既往,如果你遇到任何问题,请告诉我们! + + +[@leonitousconforti]: https://github.com/leonitousconforti +[@hsorbo]: https://x.com/hsorbo diff --git a/_i18n/cn/_posts/2025-11-05-frida-17-5-1-released.markdown b/_i18n/cn/_posts/2025-11-05-frida-17-5-1-released.markdown new file mode 100644 index 00000000..f8bfbbad --- /dev/null +++ b/_i18n/cn/_posts/2025-11-05-frida-17-5-1-released.markdown @@ -0,0 +1,19 @@ +--- +layout: news_item +title: 'Frida 17.5.1 发布' +date: 2025-11-05 18:59:20 +0100 +author: oleavr +version: 17.5.1 +categories: [release] +--- + +新鲜出炉:Frida 17.5.1!🍞 + +此版本包含一个重要的错误修复: + +- **darwin-mapper**: 添加了对本地共享缓存查找的验证。看似存在于缓存中的符号不再能解析为潜伏在缓存之外的 dylib(例如 `libsystem_pthread.dylib` 的内省构建)。非常感谢 [@hsorbo][] 协助调查! + +享受黑客乐趣!🧠💥 + + +[@hsorbo]: https://x.com/hsorbo