Compare commits
No files matched your search
@@ -0,0 +1,4 @@
|
||||
set(VCPKG_TARGET_ARCHITECTURE arm64)
|
||||
set(VCPKG_CRT_LINKAGE dynamic)
|
||||
set(VCPKG_LIBRARY_LINKAGE dynamic)
|
||||
set(VCPKG_BUILD_TYPE release)
|
||||
@@ -0,0 +1,4 @@
|
||||
set(VCPKG_TARGET_ARCHITECTURE x64)
|
||||
set(VCPKG_CRT_LINKAGE dynamic)
|
||||
set(VCPKG_LIBRARY_LINKAGE dynamic)
|
||||
set(VCPKG_BUILD_TYPE release)
|
||||
@@ -61,13 +61,17 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-multilib lib32stdc++-13-dev
|
||||
- name: Install setuptools for distutils shim
|
||||
if: ${{ startsWith(matrix.id, 'windows-') }}
|
||||
run: pip install setuptools
|
||||
- name: Build
|
||||
if: ${{ startsWith(matrix.id, 'windows-') }}
|
||||
run: |
|
||||
.\configure `
|
||||
--host=${{ matrix.id }} `
|
||||
--enable-compiler-backend `
|
||||
${{ env.FRIDA_CORE_OPTIONS }}
|
||||
${{ env.FRIDA_CORE_OPTIONS }} `
|
||||
-- --force-fallback-for=openssl
|
||||
.\make
|
||||
env:
|
||||
MSYS2_LOCATION: ${{ steps.msys2.outputs.msys2-location }}
|
||||
@@ -493,6 +497,7 @@ jobs:
|
||||
|
||||
xcframework:
|
||||
name: Build FridaCore.xcframework
|
||||
if: ${{ !cancelled() }}
|
||||
runs-on: macos-latest
|
||||
needs:
|
||||
- native
|
||||
@@ -585,7 +590,7 @@ jobs:
|
||||
path: FridaCore.xcframework
|
||||
|
||||
publish-xcframework:
|
||||
if: github.ref_type == 'tag'
|
||||
if: ${{ !cancelled() && github.ref_type == 'tag' && needs.xcframework.result == 'success' }}
|
||||
runs-on: macos-latest
|
||||
needs:
|
||||
- xcframework
|
||||
@@ -621,3 +626,83 @@ jobs:
|
||||
prerelease: ${{ contains(github.ref_name, 'snapshot') }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
windows-shared:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- { arch: x86_64, runner: windows-latest, msvc-arch: amd64, vcpkg-triplet: x64-windows-release }
|
||||
- { arch: arm64, runner: windows-11-arm, msvc-arch: arm64, vcpkg-triplet: arm64-windows-release }
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: ${{ matrix.msvc-arch }}
|
||||
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install build tools
|
||||
run: |
|
||||
pip install meson ninja setuptools
|
||||
choco install pkgconfiglite -y
|
||||
|
||||
- name: Restore vcpkg cache
|
||||
id: vcpkg-cache
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: C:\vcpkg\installed
|
||||
key: vcpkg-${{ matrix.vcpkg-triplet }}-v5
|
||||
|
||||
- name: Install dependencies via vcpkg
|
||||
if: steps.vcpkg-cache.outputs.cache-hit != 'true'
|
||||
run: >-
|
||||
vcpkg install
|
||||
--overlay-triplets=${{ github.workspace }}/.github/vcpkg-triplets
|
||||
--triplet ${{ matrix.vcpkg-triplet }}
|
||||
glib json-glib libsoup
|
||||
|
||||
- name: Save vcpkg cache
|
||||
if: steps.vcpkg-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@v5
|
||||
with:
|
||||
path: C:\vcpkg\installed
|
||||
key: vcpkg-${{ matrix.vcpkg-triplet }}-v5
|
||||
|
||||
- name: Set up vcpkg environment
|
||||
shell: bash
|
||||
run: |
|
||||
TRIPLET="${{ matrix.vcpkg-triplet }}"
|
||||
VCPKG_PREFIX="C:/vcpkg/installed/$TRIPLET"
|
||||
echo "$VCPKG_PREFIX/tools/pkgconf" >> $GITHUB_PATH
|
||||
echo "$VCPKG_PREFIX/bin" >> $GITHUB_PATH
|
||||
echo "$VCPKG_PREFIX/tools" >> $GITHUB_PATH
|
||||
echo "PKG_CONFIG_PATH=$VCPKG_PREFIX/lib/pkgconfig" >> $GITHUB_ENV
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
python -c "import sys; sys.path.insert(0, '.'); from releng.meson_configure import main; main()" `
|
||||
. `
|
||||
--host=windows-${{ matrix.arch }} `
|
||||
--enable-shared `
|
||||
--without-prebuilds=sdk `
|
||||
"--prefix=$env:RUNNER_TEMP\frida-prefix" `
|
||||
-- --force-fallback-for=openssl,libnghttp2 -Dcompiler_backend=disabled -Dtests=disabled
|
||||
|
||||
- name: Compile
|
||||
run: python releng/meson/meson.py compile -C build
|
||||
|
||||
- name: Install
|
||||
run: python releng/meson/meson.py install -C build --no-rebuild --skip-subprojects
|
||||
|
||||
- name: List installed files
|
||||
shell: bash
|
||||
run: find "$RUNNER_TEMP/frida-prefix" -type f | sort
|
||||
@@ -0,0 +1,153 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code)
|
||||
when working with code in this repository.
|
||||
|
||||
## What This Is
|
||||
|
||||
frida-core is the core library of the Frida dynamic
|
||||
instrumentation toolkit. It packages GumJS into a shared
|
||||
library, injects JavaScript instrumentation into target
|
||||
processes, provides bidirectional RPC communication with
|
||||
injected scripts, and enumerates apps/processes/devices.
|
||||
Written primarily in Vala with OS-specific glue in
|
||||
C/Objective-C/asm.
|
||||
|
||||
## Build Commands
|
||||
|
||||
```bash
|
||||
# First-time setup (fetches git submodules, configures build):
|
||||
./configure \
|
||||
--enable-compiler-backend \
|
||||
--with-devkits=core \
|
||||
--enable-tests
|
||||
|
||||
# Build:
|
||||
make
|
||||
|
||||
# Run tests:
|
||||
make test
|
||||
|
||||
# Rebuild after code changes
|
||||
make
|
||||
```
|
||||
|
||||
The `configure` script and `Makefile` are thin wrappers
|
||||
around `releng/meson_configure.py` and
|
||||
`releng/meson_make.py`. The actual build system is Meson
|
||||
(>=1.1.0).
|
||||
|
||||
Build artifacts go into `build/`. The triplet is
|
||||
platform-dependent (e.g. `linux-x86_64`).
|
||||
|
||||
## Build Requirements
|
||||
|
||||
- **Vala compiler**: Must be the Frida-optimized fork from
|
||||
https://github.com/frida/vala (checks for `-frida` suffix
|
||||
in version)
|
||||
- **C/C++ standards**: gnu99/c99 for C, c++17 for C++
|
||||
- **Go >= 1.24**: Required for compiler backend
|
||||
- **Python 3**: Build scripts
|
||||
|
||||
## Architecture
|
||||
|
||||
### Backend System
|
||||
|
||||
The core abstraction is a pluggable backend system. Each
|
||||
backend implements `HostSessionBackend` and provides a
|
||||
`HostSessionProvider` to create sessions with target
|
||||
processes:
|
||||
|
||||
- **local** — Injects into processes on the local system
|
||||
(Linux, macOS, Windows, FreeBSD, QNX)
|
||||
- **fruity** — iOS/tvOS/xrOS devices via USB/network
|
||||
(QUIC, lockdown protocols)
|
||||
- **droidy** — Android devices via ADB/JDWP
|
||||
- **socket** — Remote devices via TCP (connects to
|
||||
frida-server, frida-gadget, frida-portal)
|
||||
- **barebone** — Minimal backend using QuickJS
|
||||
- **simmy** — macOS Simulator
|
||||
- **compiler** — TypeScript/ESBuild compilation backend
|
||||
(Go + TypeScript in `src/compiler/`)
|
||||
|
||||
Backends are selected at build time via meson options
|
||||
(e.g. `-Dlocal_backend=enabled`).
|
||||
|
||||
### Key Source Layout
|
||||
|
||||
- **`src/frida.vala`** — Public API: `DeviceManager`,
|
||||
`Device`, `Session`, `Script`
|
||||
- **`src/host-session-service.vala`** — Backend
|
||||
orchestration, provider registration
|
||||
- **`src/control-service.vala`** — the guts of frida-server
|
||||
- **`src/portal-service.vala`** — the guts of frida-portal,
|
||||
which looks like a frida-server that surfaces any joined
|
||||
processes (think aggregator/reverse proxy)
|
||||
- **`lib/base/`** — Core primitives: Session, RPC, streams,
|
||||
sockets, P2P, buffers
|
||||
- **`lib/agent/`** — In-process JavaScript agent runtime
|
||||
- **`lib/payload/`** — Payload compilation and embedding
|
||||
- **`lib/gadget/`** — Gadget (preloaded library)
|
||||
implementation
|
||||
- **`src/<platform>/`** — Platform-specific injectors and
|
||||
helpers (`linux/`, `darwin/`, `windows/`, `freebsd/`,
|
||||
`qnx/`)
|
||||
- **`src/fruity/`** — iOS device protocol stack (USB, QUIC,
|
||||
plist, XPC, LLDB)
|
||||
- **`src/droidy/`** — Android device protocol stack
|
||||
- **`src/compiler/`** — Go/TypeScript compiler backend
|
||||
|
||||
### Injection Flow
|
||||
|
||||
Each platform has its own injector (e.g. `Linjector` on
|
||||
Linux, `Fruitjector` on Darwin, `Winjector` on Windows). A
|
||||
helper process (`frida-helper`) performs privileged injection
|
||||
operations out-of-process. Helpers and agents can be embedded
|
||||
into the library or installed as separate files (controlled
|
||||
by the `assets` meson option).
|
||||
|
||||
### Deliverables
|
||||
|
||||
- **frida-server** (`server/`) — Standalone daemon exposing
|
||||
the full API over the network
|
||||
- **frida-portal** (`portal/`) — Cluster node for
|
||||
distributed instrumentation
|
||||
- **frida-inject** (`inject/`) — CLI tool for one-shot
|
||||
script injection
|
||||
- **frida-gadget** (`lib/gadget/`) — Shared library for
|
||||
preload-based instrumentation
|
||||
|
||||
### Resource Embedding
|
||||
|
||||
Build artifacts (frida-helper, frida-agent) are compiled
|
||||
into resource blobs via `tools/resource-compiler.vala` and
|
||||
embedding scripts (`src/embed-helper.py`,
|
||||
`src/embed-agent.py`). On non-MSVC toolchains these become
|
||||
`.S` assembly files; on MSVC they become `.obj` files.
|
||||
|
||||
## Tests
|
||||
|
||||
Tests are in `tests/` as Vala files using a custom async
|
||||
test harness (`tests/async-harness.vala`). Test binary is
|
||||
`tests/frida-tests`. Platform-specific runner scripts exist
|
||||
(e.g. `tests/run-linux-x86_64.sh`).
|
||||
|
||||
Test modules: `test-system`, `test-injector`, `test-agent`,
|
||||
`test-host-session`, `test-gadget`, `test-compiler`. Test
|
||||
helper programs ("labrats") are in `tests/labrats/`.
|
||||
|
||||
## Code Style
|
||||
|
||||
### Function Order
|
||||
|
||||
Use newspaper/stepdown order: high-level entry points
|
||||
first, low-level helpers and constants last. In C,
|
||||
`main()` is always first. When ordering siblings at the
|
||||
same level, use the chronological order of first use
|
||||
within their caller.
|
||||
|
||||
## Commit Style
|
||||
|
||||
- Subject line: max 50 characters
|
||||
- Body lines: wrap at 72 characters (use the full width, or
|
||||
slightly less if it avoids making the next line awkward)
|
||||
@@ -1,15 +1,18 @@
|
||||
# frida-core
|
||||
|
||||
Frida core library intended for static linking into bindings.
|
||||
Frida core library.
|
||||
|
||||
- Lets you inject your own JavaScript instrumentation code into other processes,
|
||||
optionally with your own [C code][] for performance-sensitive bits.
|
||||
- Acts as a logistics layer that packages up [GumJS][] into a shared library.
|
||||
- Provides a two-way communication channel for talking to your scripts,
|
||||
if needed, and later unload them.
|
||||
- Also lets you enumerate installed apps, running processes, and connected
|
||||
devices.
|
||||
- Written in [Vala][], with OS-specific glue code in C/Objective-C/asm.
|
||||
- Lets you inject your own JavaScript instrumentation code
|
||||
into other processes, optionally with your own [C code][]
|
||||
for performance-sensitive bits.
|
||||
- Acts as a logistics layer that packages up [GumJS][] into
|
||||
a shared library.
|
||||
- Provides a two-way communication channel for talking to
|
||||
your scripts, if needed, and later unload them.
|
||||
- Also lets you enumerate installed apps, running processes,
|
||||
and connected devices.
|
||||
- Written in [Vala][], with OS-specific glue code in
|
||||
C/Objective-C/asm.
|
||||
|
||||
## Binaries
|
||||
|
||||
@@ -29,8 +32,43 @@ $ pip install frida # Python bindings
|
||||
$ npm install frida # Node.js bindings
|
||||
```
|
||||
|
||||
Or, for static linking into your own project written in a C-compatible language,
|
||||
download a devkit from the Frida [releases][] page.
|
||||
Or, for static linking into your own project written in a
|
||||
C-compatible language, download a devkit from the Frida
|
||||
[releases][] page.
|
||||
|
||||
## Distro Packaging
|
||||
|
||||
For building a shared library suitable for distro packaging:
|
||||
|
||||
```console
|
||||
$ ./configure --enable-shared --without-prebuilds=sdk && make
|
||||
```
|
||||
|
||||
This builds `libfrida-core-1.0.so` against system dependencies,
|
||||
while `frida-agent.so` is built using Frida's SDK (with its GLib
|
||||
fork, needed for the agent's inject/unload lifecycle).
|
||||
|
||||
Prerequisites on Fedora:
|
||||
|
||||
```console
|
||||
$ sudo dnf install libgee-devel json-glib-devel \
|
||||
libsoup3-devel libunwind-devel libdwarf-devel \
|
||||
libnice-devel ngtcp2-crypto-ossl-devel
|
||||
```
|
||||
|
||||
Prerequisites on Ubuntu:
|
||||
|
||||
```console
|
||||
$ sudo apt install libgee-0.8-dev libjson-glib-dev \
|
||||
libsoup-3.0-dev libunwind-dev libdwarf-dev \
|
||||
libnice-dev libngtcp2-crypto-ossl-dev
|
||||
```
|
||||
|
||||
Prerequisites on macOS:
|
||||
|
||||
```console
|
||||
$ brew install libgee json-glib libsoup libnice capstone
|
||||
```
|
||||
|
||||
## Internals
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import os
|
||||
from pathlib import Path
|
||||
import pickle
|
||||
import platform
|
||||
import re
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
@@ -35,6 +36,7 @@ def main(argv):
|
||||
command.add_argument("top_builddir", help="top build directory", type=Path)
|
||||
command.add_argument("frida_version", help="the Frida version")
|
||||
command.add_argument("host_os", help="operating system binaries are being built for")
|
||||
command.add_argument("host_os_family", help="operating system family, e.g. darwin")
|
||||
command.add_argument("host_arch", help="architecture binaries are being built for")
|
||||
command.add_argument("host_config", help="configuration binaries are being built for")
|
||||
command.add_argument("compat", help="support for targets with a different architecture",
|
||||
@@ -42,17 +44,20 @@ def main(argv):
|
||||
command.add_argument("assets", help="whether assets are embedded vs installed and loaded at runtime")
|
||||
command.add_argument("components", help="which components will be built",
|
||||
type=parse_array_option_value)
|
||||
command.add_argument("glib_flavor", help="upstream or frida", choices=["upstream", "frida"])
|
||||
command.add_argument("compilers", help="compiler command arrays", nargs="+")
|
||||
command.set_defaults(func=lambda args: setup(args.role,
|
||||
args.builddir,
|
||||
args.top_builddir,
|
||||
args.frida_version,
|
||||
args.host_os,
|
||||
args.host_os_family,
|
||||
args.host_arch,
|
||||
args.host_config if args.host_config else None,
|
||||
args.compat,
|
||||
args.assets,
|
||||
args.components,
|
||||
args.glib_flavor,
|
||||
parse_compilers(args.compilers)))
|
||||
|
||||
command = subparsers.add_parser("compile", help="compile compatibility assets")
|
||||
@@ -113,11 +118,13 @@ def setup(role: Role,
|
||||
top_builddir: Path,
|
||||
frida_version: str,
|
||||
host_os: str,
|
||||
host_os_family: str,
|
||||
host_arch: str,
|
||||
host_config: Optional[str],
|
||||
compat: set[str],
|
||||
assets: str,
|
||||
components: set[str],
|
||||
glib_flavor: str,
|
||||
compilers: Compilers):
|
||||
try:
|
||||
outputs: Mapping[str, Sequence[Output]] = OrderedDict()
|
||||
@@ -192,16 +199,21 @@ def setup(role: Role,
|
||||
|
||||
if host_os == "windows" and host_arch in {"x86_64", "x86"} and have_toolchain:
|
||||
group = OutputGroup(other_label, other_triplet, extra_environ)
|
||||
outputs[group] = [
|
||||
Output(identifier=f"helper_{other_kind}",
|
||||
name=HELPER_FILE_WINDOWS.name,
|
||||
file=HELPER_FILE_WINDOWS,
|
||||
target=HELPER_TARGET),
|
||||
Output(identifier=f"agent_{other_kind}",
|
||||
name=AGENT_FILE_WINDOWS.name,
|
||||
file=AGENT_FILE_WINDOWS,
|
||||
target=AGENT_TARGET),
|
||||
]
|
||||
outputs[group] = []
|
||||
if "helper" in components:
|
||||
outputs[group] += [
|
||||
Output(identifier=f"helper_{other_kind}",
|
||||
name=HELPER_FILE_WINDOWS.name,
|
||||
file=HELPER_FILE_WINDOWS,
|
||||
target=HELPER_TARGET),
|
||||
]
|
||||
if "agent" in components:
|
||||
outputs[group] += [
|
||||
Output(identifier=f"agent_{other_kind}",
|
||||
name=AGENT_FILE_WINDOWS.name,
|
||||
file=AGENT_FILE_WINDOWS,
|
||||
target=AGENT_TARGET),
|
||||
]
|
||||
if "gadget" in components:
|
||||
outputs[group] += [
|
||||
Output(identifier=f"gadget_{other_kind}",
|
||||
@@ -218,16 +230,21 @@ def setup(role: Role,
|
||||
other_arch = "arm64e"
|
||||
kind = "modern"
|
||||
group = OutputGroup(other_arch)
|
||||
outputs[group] = [
|
||||
Output(identifier=f"helper_{kind}",
|
||||
name=f"frida-helper-{other_arch}",
|
||||
file=HELPER_FILE_UNIX,
|
||||
target=HELPER_TARGET),
|
||||
Output(identifier=f"agent_{kind}",
|
||||
name=f"frida-agent-{other_arch}.dylib",
|
||||
file=AGENT_FILE_DARWIN,
|
||||
target=AGENT_TARGET),
|
||||
]
|
||||
outputs[group] = []
|
||||
if "helper" in components:
|
||||
outputs[group] += [
|
||||
Output(identifier=f"helper_{kind}",
|
||||
name=f"frida-helper-{other_arch}",
|
||||
file=HELPER_FILE_UNIX,
|
||||
target=HELPER_TARGET),
|
||||
]
|
||||
if "agent" in components:
|
||||
outputs[group] += [
|
||||
Output(identifier=f"agent_{kind}",
|
||||
name=f"frida-agent-{other_arch}.dylib",
|
||||
file=AGENT_FILE_DARWIN,
|
||||
target=AGENT_TARGET),
|
||||
]
|
||||
if "gadget" in components:
|
||||
outputs[group] += [
|
||||
Output(identifier=f"gadget_{kind}",
|
||||
@@ -245,16 +262,21 @@ def setup(role: Role,
|
||||
|
||||
if host_os == "linux" and host_arch == "x86_64" and have_toolchain:
|
||||
group = OutputGroup("x86", other_triplet, extra_environ)
|
||||
outputs[group] = [
|
||||
Output(identifier="helper_legacy",
|
||||
name=HELPER_FILE_UNIX.name,
|
||||
file=HELPER_FILE_UNIX,
|
||||
target=HELPER_TARGET),
|
||||
Output(identifier="agent_legacy",
|
||||
name=AGENT_FILE_ELF.name,
|
||||
file=AGENT_FILE_ELF,
|
||||
target=AGENT_TARGET),
|
||||
]
|
||||
outputs[group] = []
|
||||
if "helper" in components:
|
||||
outputs[group] += [
|
||||
Output(identifier="helper_legacy",
|
||||
name=HELPER_FILE_UNIX.name,
|
||||
file=HELPER_FILE_UNIX,
|
||||
target=HELPER_TARGET),
|
||||
]
|
||||
if "agent" in components:
|
||||
outputs[group] += [
|
||||
Output(identifier="agent_legacy",
|
||||
name=AGENT_FILE_ELF.name,
|
||||
file=AGENT_FILE_ELF,
|
||||
target=AGENT_TARGET),
|
||||
]
|
||||
if "gadget" in components:
|
||||
outputs[group] += [
|
||||
Output(identifier="gadget_legacy",
|
||||
@@ -265,16 +287,21 @@ def setup(role: Role,
|
||||
|
||||
if host_os == "android" and host_arch in {"arm64", "x86_64"}:
|
||||
group = OutputGroup("arm" if host_arch == "arm64" else "x86")
|
||||
outputs[group] = [
|
||||
Output(identifier="helper_legacy",
|
||||
name=HELPER_FILE_UNIX.name,
|
||||
file=HELPER_FILE_UNIX,
|
||||
target=HELPER_TARGET),
|
||||
Output(identifier="agent_legacy",
|
||||
name=AGENT_FILE_ELF.name,
|
||||
file=AGENT_FILE_ELF,
|
||||
target=AGENT_TARGET),
|
||||
]
|
||||
outputs[group] = []
|
||||
if "helper" in components:
|
||||
outputs[group] += [
|
||||
Output(identifier="helper_legacy",
|
||||
name=HELPER_FILE_UNIX.name,
|
||||
file=HELPER_FILE_UNIX,
|
||||
target=HELPER_TARGET),
|
||||
]
|
||||
if "agent" in components:
|
||||
outputs[group] += [
|
||||
Output(identifier="agent_legacy",
|
||||
name=AGENT_FILE_ELF.name,
|
||||
file=AGENT_FILE_ELF,
|
||||
target=AGENT_TARGET),
|
||||
]
|
||||
if "gadget" in components:
|
||||
outputs[group] += [
|
||||
Output(identifier="gadget_legacy",
|
||||
@@ -295,18 +322,24 @@ def setup(role: Role,
|
||||
continue
|
||||
else:
|
||||
emulated_triplet = None
|
||||
outputs[OutputGroup(emulated_arch, emulated_triplet, extra_environ)] = [
|
||||
Output(identifier=f"helper_emulated_{kind}",
|
||||
name=HELPER_FILE_WINDOWS.name.replace(".exe", f"-{emulated_arch}.exe"),
|
||||
file=HELPER_FILE_WINDOWS,
|
||||
target=HELPER_TARGET),
|
||||
Output(identifier=f"agent_emulated_{kind}",
|
||||
name=AGENT_FILE_WINDOWS.name.replace(".dll", f"-{emulated_arch}.dll"),
|
||||
file=AGENT_FILE_WINDOWS,
|
||||
target=AGENT_TARGET),
|
||||
]
|
||||
group = OutputGroup(emulated_arch, emulated_triplet, extra_environ)
|
||||
outputs[group] = []
|
||||
if "helper" in components:
|
||||
outputs[group] += [
|
||||
Output(identifier=f"helper_emulated_{kind}",
|
||||
name=HELPER_FILE_WINDOWS.name.replace(".exe", f"-{emulated_arch}.exe"),
|
||||
file=HELPER_FILE_WINDOWS,
|
||||
target=HELPER_TARGET),
|
||||
]
|
||||
if "agent" in components:
|
||||
outputs[group] += [
|
||||
Output(identifier=f"agent_emulated_{kind}",
|
||||
name=AGENT_FILE_WINDOWS.name.replace(".dll", f"-{emulated_arch}.dll"),
|
||||
file=AGENT_FILE_WINDOWS,
|
||||
target=AGENT_TARGET),
|
||||
]
|
||||
|
||||
if host_os == "android" and host_arch in {"x86_64", "x86"}:
|
||||
if host_os == "android" and host_arch in {"x86_64", "x86"} and "agent" in components:
|
||||
outputs[OutputGroup("arm")] = [
|
||||
Output(identifier="agent_emulated_legacy",
|
||||
name="frida-agent-arm.so",
|
||||
@@ -321,10 +354,35 @@ def setup(role: Role,
|
||||
target=AGENT_TARGET),
|
||||
]
|
||||
|
||||
raw_allowed_prebuilds = os.environ.get("FRIDA_ALLOWED_PREBUILDS")
|
||||
allowed_prebuilds = set(raw_allowed_prebuilds.split(",")) if raw_allowed_prebuilds is not None else None
|
||||
if glib_flavor == "upstream":
|
||||
kind = "modern" if arch_is_modern(host_os, host_arch) else "legacy"
|
||||
_, agent_file, gadget_file = native_file_paths(host_os)
|
||||
group = OutputGroup(host_arch)
|
||||
outputs[group] = []
|
||||
if "agent" in components:
|
||||
outputs[group] += [
|
||||
Output(identifier=f"agent_{kind}",
|
||||
name=arch_suffixed_name(agent_file.name, host_arch),
|
||||
file=agent_file,
|
||||
target=AGENT_TARGET),
|
||||
]
|
||||
if "gadget" in components:
|
||||
outputs[group] += [
|
||||
Output(identifier=f"gadget_{kind}",
|
||||
name=arch_suffixed_name(gadget_file.name, host_arch),
|
||||
file=gadget_file,
|
||||
target=GADGET_TARGET),
|
||||
]
|
||||
|
||||
state = State(role, builddir, top_builddir, frida_version, host_os, host_config, allowed_prebuilds, outputs)
|
||||
outputs = OrderedDict((g, items) for g, items in outputs.items() if items)
|
||||
|
||||
raw_allowed_prebuilds = os.environ.get("FRIDA_ALLOWED_PREBUILDS")
|
||||
if raw_allowed_prebuilds is not None:
|
||||
allowed_prebuilds = set(filter(None, raw_allowed_prebuilds.split(",")))
|
||||
else:
|
||||
allowed_prebuilds = None
|
||||
|
||||
state = State(role, builddir, top_builddir, frida_version, host_os, host_os_family, host_config, glib_flavor, allowed_prebuilds, outputs)
|
||||
serialized_state = base64.b64encode(pickle.dumps(state)).decode('ascii')
|
||||
|
||||
if missing:
|
||||
@@ -360,7 +418,9 @@ class State:
|
||||
top_builddir: Path
|
||||
frida_version: str
|
||||
host_os: str
|
||||
host_os_family: str
|
||||
host_config: Optional[str]
|
||||
glib_flavor: str
|
||||
allowed_prebuilds: Optional[set[str]]
|
||||
outputs: Mapping[OutputGroup, Sequence[Output]]
|
||||
|
||||
@@ -440,20 +500,33 @@ def compile(privdir: Path, state: State):
|
||||
|
||||
host_machine = MachineSpec(state.host_os, group.arch, state.host_config, group.triplet)
|
||||
|
||||
if state.glib_flavor == "upstream":
|
||||
allowed = None
|
||||
else:
|
||||
allowed = state.allowed_prebuilds
|
||||
|
||||
if state.glib_flavor == "upstream":
|
||||
forwarded_options = [o for o in options if not is_stripped_compat_option(o)]
|
||||
forwarded_options += ["-Ddefault_library=static"]
|
||||
else:
|
||||
forwarded_options = [o for o in options if not is_overridden_compat_option(o)]
|
||||
|
||||
configure(sourcedir=REPO_ROOT,
|
||||
builddir=workdir,
|
||||
host_machine=host_machine,
|
||||
environ={**build_env, **group.extra_environ},
|
||||
allowed_prebuilds=state.allowed_prebuilds,
|
||||
allowed_prebuilds=allowed,
|
||||
apple_min_os=detect_parent_apple_min_os(top_builddir, state.host_os, state.host_os_family),
|
||||
extra_meson_options=[
|
||||
"-Dcompiler_backend=disabled",
|
||||
"-Dassets=installed",
|
||||
"-Dhelper_modern=",
|
||||
"-Dhelper_legacy=",
|
||||
"-Dagent_modern=",
|
||||
"-Dagent_legacy=",
|
||||
"-Dagent_emulated_modern=",
|
||||
"-Dagent_emulated_legacy=",
|
||||
*options,
|
||||
*forwarded_options,
|
||||
],
|
||||
call_meson=call_internal_meson,
|
||||
on_progress=lambda progress: None)
|
||||
@@ -484,6 +557,18 @@ def compile(privdir: Path, state: State):
|
||||
(state.builddir / DEPFILE_FILENAME).write_text("\n".join(depfile_lines), encoding="utf-8")
|
||||
|
||||
|
||||
def is_stripped_compat_option(o: str) -> bool:
|
||||
if is_overridden_compat_option(o):
|
||||
return True
|
||||
name = o.split("=", 1)[0].removeprefix("-D")
|
||||
return name in STRIPPED_COMPAT_OPTIONS or name.endswith(":default_library")
|
||||
|
||||
|
||||
def is_overridden_compat_option(o: str) -> bool:
|
||||
name = o.split("=", 1)[0].removeprefix("-D")
|
||||
return name in OVERRIDDEN_COMPAT_OPTIONS
|
||||
|
||||
|
||||
def load_meson_options(top_builddir: Path,
|
||||
role: Role,
|
||||
subprojects: set[str]) -> Sequence[str]:
|
||||
@@ -593,6 +678,19 @@ def ensure_submodules_checked_out(releng_location: Path):
|
||||
check=True)
|
||||
|
||||
|
||||
def detect_parent_apple_min_os(top_builddir: Path,
|
||||
host_os: str,
|
||||
host_os_family: str) -> Optional[Mapping[str, str]]:
|
||||
if host_os_family != "darwin":
|
||||
return None
|
||||
pattern = re.compile(r"-target['\"],\s*['\"][^-]+-apple-" + re.escape(host_os) + r"([0-9.]+)")
|
||||
for native_file in top_builddir.glob(f"frida-{host_os}-*.txt"):
|
||||
match = pattern.search(native_file.read_text(encoding="utf-8"))
|
||||
if match is not None:
|
||||
return {host_os: match.group(1)}
|
||||
return None
|
||||
|
||||
|
||||
def detect_relevant_subprojects(releng_location: Path) -> dict[str, Path]:
|
||||
subprojects = detect_relevant_subprojects_in(REPO_ROOT, releng_location)
|
||||
gum_location = subprojects.get("frida-gum")
|
||||
@@ -643,9 +741,48 @@ def detect_mingw_toolchain_for(arch: str) -> Tuple[bool, Optional[str]]:
|
||||
return (found, triplet)
|
||||
|
||||
|
||||
def arch_is_modern(host_os: str, host_arch: str) -> bool:
|
||||
if host_os in {"macos", "ios", "tvos"} and host_arch == "arm64":
|
||||
return host_arch == "arm64e"
|
||||
return host_arch in MODERN_ARCHS
|
||||
|
||||
|
||||
def native_file_paths(host_os: str) -> Tuple[Path, Path, Path]:
|
||||
if host_os == "windows":
|
||||
return (HELPER_FILE_WINDOWS, AGENT_FILE_WINDOWS, GADGET_FILE_WINDOWS)
|
||||
elif host_os in {"macos", "ios", "tvos"}:
|
||||
return (HELPER_FILE_UNIX, AGENT_FILE_DARWIN, GADGET_FILE_DARWIN)
|
||||
else:
|
||||
return (HELPER_FILE_UNIX, AGENT_FILE_ELF, GADGET_FILE_ELF)
|
||||
|
||||
|
||||
def arch_suffixed_name(name: str, arch: str) -> str:
|
||||
stem, _, ext = name.partition(".")
|
||||
if ext:
|
||||
return f"{stem}-{arch}.{ext}"
|
||||
return f"{stem}-{arch}"
|
||||
|
||||
|
||||
STATE_FILENAME = "state.dat"
|
||||
DEPFILE_FILENAME = "compat.deps"
|
||||
|
||||
STRIPPED_COMPAT_OPTIONS = {
|
||||
"default_library",
|
||||
"frida-gum:gumjs",
|
||||
"frida-gum:inspector",
|
||||
}
|
||||
|
||||
OVERRIDDEN_COMPAT_OPTIONS = {
|
||||
"compiler_backend",
|
||||
"assets",
|
||||
"helper_modern",
|
||||
"helper_legacy",
|
||||
"agent_modern",
|
||||
"agent_legacy",
|
||||
"agent_emulated_modern",
|
||||
"agent_emulated_legacy",
|
||||
}
|
||||
|
||||
HELPER_TARGET = "frida-helper"
|
||||
HELPER_FILE_WINDOWS = Path("src") / "frida-helper.exe"
|
||||
HELPER_FILE_UNIX = Path("src") / "frida-helper"
|
||||
@@ -670,6 +807,8 @@ MSVS_ENVVARS = {
|
||||
"LIB",
|
||||
}
|
||||
|
||||
MODERN_ARCHS = {"x86_64", "arm64", "arm64e", "mips64", "riscv64"}
|
||||
|
||||
MINGW_ARCHS = {
|
||||
"x86": "i686",
|
||||
"arm64": "aarch64",
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
src = sys.argv[1]
|
||||
install_subdir = sys.argv[2]
|
||||
install_name = sys.argv[3]
|
||||
|
||||
destdir_prefix = os.environ["MESON_INSTALL_DESTDIR_PREFIX"]
|
||||
dst_dir = os.path.join(destdir_prefix, install_subdir)
|
||||
dst = os.path.join(dst_dir, install_name)
|
||||
|
||||
os.makedirs(dst_dir, exist_ok=True)
|
||||
shutil.copy2(src, dst)
|
||||
print(f"Installing {os.path.basename(src)} to {dst_dir}/{install_name}")
|
||||
@@ -28,6 +28,9 @@ if no_overridden_compat_bits
|
||||
build_py = [python, files('build.py')]
|
||||
|
||||
components = []
|
||||
if have_local_backend
|
||||
components += ['helper', 'agent']
|
||||
endif
|
||||
if build_gadget
|
||||
components += 'gadget'
|
||||
endif
|
||||
@@ -42,11 +45,13 @@ if no_overridden_compat_bits
|
||||
meson.global_build_root(),
|
||||
frida_version,
|
||||
host_os,
|
||||
host_os_family,
|
||||
host_abi,
|
||||
host_config,
|
||||
','.join(get_option('compat')),
|
||||
get_option('assets'),
|
||||
assets_mode,
|
||||
','.join(components),
|
||||
have_shared_glib ? 'upstream' : 'frida',
|
||||
'--',
|
||||
'>>>', cc.cmd_array(), '<<<',
|
||||
'>>>', cpp.cmd_array(), '<<<',
|
||||
@@ -64,18 +69,27 @@ if no_overridden_compat_bits
|
||||
depfile = lines[4]
|
||||
state = lines[5]
|
||||
|
||||
native_arch_suffix = '-' + host_abi
|
||||
|
||||
if host_os_family == 'darwin'
|
||||
install = false
|
||||
install_dir = asset_dir
|
||||
else
|
||||
install = true
|
||||
install_dir = []
|
||||
i = 0
|
||||
foreach varname : varnames
|
||||
if varname != 'arch_support_bundle' and (get_option('assets') == 'installed' or varname.startswith('gadget_'))
|
||||
output_name = outputs[i]
|
||||
needs_rename = output_name.contains(native_arch_suffix) or varname.contains('_emulated_')
|
||||
should_install = varname != 'arch_support_bundle' \
|
||||
and not needs_rename \
|
||||
and (assets_mode == 'installed' or varname.startswith('gadget_'))
|
||||
if should_install
|
||||
install_dir += varname.endswith('_modern') ? asset_dir_modern : asset_dir_legacy
|
||||
else
|
||||
install_dir += false
|
||||
endif
|
||||
i += 1
|
||||
endforeach
|
||||
endif
|
||||
|
||||
@@ -96,6 +110,7 @@ if no_overridden_compat_bits
|
||||
foreach output : arch_support.to_list()
|
||||
outpath = output.full_path()
|
||||
varname = varnames[i]
|
||||
output_name = outputs[i]
|
||||
|
||||
set_variable(varname, outpath)
|
||||
|
||||
@@ -115,6 +130,29 @@ if no_overridden_compat_bits
|
||||
assert(false, f'unexpected variable name: @varname@')
|
||||
endif
|
||||
|
||||
if host_os_family != 'darwin'
|
||||
rename_target_dir = ''
|
||||
if output_name.contains(native_arch_suffix)
|
||||
rename_target_dir = asset_dir
|
||||
elif varname.contains('_emulated_')
|
||||
emulated_arch = output_name.split('-')[-1].split('.')[0]
|
||||
rename_target_dir = root_asset_dir / emulated_arch
|
||||
endif
|
||||
if rename_target_dir != ''
|
||||
install_name = output_name.replace(native_arch_suffix, '')
|
||||
foreach arch_suffix : ['-x86_64', '-x86', '-arm64', '-arm']
|
||||
install_name = install_name.replace(arch_suffix, '')
|
||||
endforeach
|
||||
if assets_mode == 'installed' or varname.startswith('gadget_')
|
||||
meson.add_install_script(python, files('install-renamed.py'),
|
||||
outpath,
|
||||
rename_target_dir,
|
||||
install_name,
|
||||
)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
i += 1
|
||||
endforeach
|
||||
else
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
# include <signal.h>
|
||||
# endif
|
||||
#endif
|
||||
#ifdef HAVE_GIOOPENSSL
|
||||
#if defined (HAVE_GIOAPPLE)
|
||||
# include <gioapple.h>
|
||||
#elif defined (HAVE_GIOOPENSSL)
|
||||
# include <gioopenssl.h>
|
||||
#endif
|
||||
|
||||
@@ -31,7 +33,9 @@ _frida_agent_environment_init (void)
|
||||
|
||||
g_thread_set_garbage_handler (_frida_agent_on_pending_thread_garbage, NULL);
|
||||
|
||||
#ifdef HAVE_GIOOPENSSL
|
||||
#if defined (HAVE_GIOAPPLE)
|
||||
g_io_module_apple_register ();
|
||||
#elif defined (HAVE_GIOOPENSSL)
|
||||
g_io_module_openssl_register ();
|
||||
#endif
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ modulated_agent = custom_target('frida-agent-modulated',
|
||||
)
|
||||
|
||||
need_universal_agent = host_os_family == 'darwin' \
|
||||
and get_option('assets') == 'installed' \
|
||||
and assets_mode == 'installed' \
|
||||
and agent_compat != ''
|
||||
|
||||
if host_os_family == 'darwin'
|
||||
@@ -80,7 +80,7 @@ agent = custom_target('frida-agent',
|
||||
output: need_universal_agent ? f'@agent_name@-@host_abi@' : agent_name,
|
||||
command: post_process + ['shared-library', identity],
|
||||
build_by_default: true,
|
||||
install: get_option('assets') == 'installed' and not need_universal_agent,
|
||||
install: assets_mode == 'installed' and not need_universal_agent,
|
||||
install_dir: asset_dir,
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
namespace Frida {
|
||||
public class AssetLocation : Object {
|
||||
public string libdir {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public static AssetLocation detect () {
|
||||
string? module_path = null;
|
||||
Gum.Address our_address = Gum.Address.from_pointer (Gum.strip_code_pointer ((void *) detect));
|
||||
|
||||
Gum.Process.enumerate_modules ((details) => {
|
||||
var range = details.range;
|
||||
if (our_address >= range.base_address && our_address < range.base_address + range.size) {
|
||||
module_path = details.path;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
assert (module_path != null);
|
||||
|
||||
string libdir = Path.get_dirname (module_path);
|
||||
#if WINDOWS
|
||||
if (Path.get_basename (libdir).down () == "bin") {
|
||||
string parent = Path.get_dirname (libdir);
|
||||
string candidate = Path.build_filename (parent, "lib");
|
||||
if (FileUtils.test (candidate, IS_DIR))
|
||||
libdir = candidate;
|
||||
}
|
||||
#endif
|
||||
|
||||
return new AssetLocation (libdir);
|
||||
}
|
||||
|
||||
private AssetLocation (string libdir) {
|
||||
Object (libdir: libdir);
|
||||
}
|
||||
|
||||
public string derive_asset_path (string arch, string filename) {
|
||||
return Path.build_filename (libdir, Config.FRIDA_LIBDIR_NAME, arch, filename);
|
||||
}
|
||||
|
||||
public string derive_plugin_path (string filename) {
|
||||
return Path.build_filename (libdir, Config.FRIDA_LIBDIR_NAME, "plugins", filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,6 +128,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_LOCAL_BACKEND
|
||||
public sealed class BpfObject {
|
||||
public Programs programs {
|
||||
get;
|
||||
@@ -522,6 +523,7 @@ namespace Frida {
|
||||
STOP,
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace PerfEvent {
|
||||
public FileDescriptor open (PerfEventAttr * attr, int pid, int cpu, int group_fd, uint flags) throws Error {
|
||||
@@ -536,7 +538,9 @@ namespace Frida {
|
||||
throw new Error.NOT_SUPPORTED ("%s (errno=%d: %s)".printf (message, Posix.errno, Posix.strerror (Posix.errno)));
|
||||
}
|
||||
|
||||
#if HAVE_LOCAL_BACKEND
|
||||
private size_t round_up_8 (size_t x) {
|
||||
return (x + 7) & ~((size_t) 7);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
base_sources = [
|
||||
'asset-location.vala',
|
||||
'session.vala',
|
||||
'session-glue.c',
|
||||
'channel.vala',
|
||||
@@ -47,8 +48,10 @@ if host_os_family == 'linux'
|
||||
extra_vala_args += [
|
||||
'--pkg=linux',
|
||||
'--pkg=frida-atomics',
|
||||
'--pkg=libbpf',
|
||||
]
|
||||
if have_local_backend
|
||||
extra_vala_args += '--pkg=libbpf'
|
||||
endif
|
||||
|
||||
syscall_arch_from_abi = {
|
||||
'armbe': 'arm',
|
||||
@@ -75,7 +78,7 @@ extra_deps = []
|
||||
if host_os_family != 'windows'
|
||||
extra_deps += gio_unix_dep
|
||||
endif
|
||||
if host_os_family == 'linux'
|
||||
if host_os_family == 'linux' and have_local_backend
|
||||
extra_deps += libbpf_dep
|
||||
endif
|
||||
|
||||
@@ -112,7 +115,7 @@ base = static_library('frida-base-' + api_version, base_sources,
|
||||
vala_gir: base_gir_name,
|
||||
vala_vapi: f'frida-base-@[email protected]',
|
||||
dependencies: [glib_dep, gobject_dep, gio_dep, libsoup_dep, nice_dep, usrsctp_dep, gee_dep, json_glib_dep, gum_dep, openssl_dep] + extra_deps,
|
||||
install: true,
|
||||
install: have_in_tree_payloads,
|
||||
install_dir: [true, header_install_dir, true, false],
|
||||
)
|
||||
|
||||
@@ -124,10 +127,12 @@ base_dep = declare_dependency(
|
||||
|
||||
base_build_dir = meson.current_build_dir()
|
||||
|
||||
pkg = import('pkgconfig')
|
||||
pkg.generate(base,
|
||||
filebase: f'frida-base-@api_version@',
|
||||
name: 'Frida Base Library',
|
||||
version: frida_version,
|
||||
description: 'Basic building blocks',
|
||||
)
|
||||
if have_in_tree_payloads
|
||||
pkg = import('pkgconfig')
|
||||
pkg.generate(base,
|
||||
filebase: f'frida-base-@api_version@',
|
||||
name: 'Frida Base Library',
|
||||
version: frida_version,
|
||||
description: 'Basic building blocks',
|
||||
)
|
||||
endif
|
||||
@@ -1,26 +1,432 @@
|
||||
#ifdef HAVE_NICE
|
||||
|
||||
#define OPENSSL_SUPPRESS_DEPRECATED
|
||||
|
||||
#include "frida-base.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <openssl/asn1.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <usrsctp.h>
|
||||
|
||||
#ifdef HAVE_GIOAPPLE
|
||||
# include <CoreFoundation/CoreFoundation.h>
|
||||
# include <Security/Security.h>
|
||||
#else
|
||||
# define OPENSSL_SUPPRESS_DEPRECATED
|
||||
# include <openssl/asn1.h>
|
||||
# include <openssl/bio.h>
|
||||
# include <openssl/bn.h>
|
||||
# include <openssl/evp.h>
|
||||
# include <openssl/pem.h>
|
||||
# include <openssl/rsa.h>
|
||||
# include <openssl/x509.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GIOAPPLE
|
||||
static SecKeyRef frida_rsa_keypair_generate (void);
|
||||
static void frida_rsa_export_public_key (SecKeyRef private_key, GByteArray * out);
|
||||
static void frida_rsa_export_private_key (SecKeyRef private_key, GByteArray * out);
|
||||
static void frida_rsa_sign_sha256 (SecKeyRef private_key, const guint8 * message, gsize message_length, GByteArray * out);
|
||||
|
||||
static void frida_build_tbs_certificate (GByteArray * out, const GByteArray * subject_public_key);
|
||||
static void frida_build_name (GByteArray * out);
|
||||
static void frida_build_rdn (GByteArray * out, const guint8 * oid, gsize oid_length, guint8 value_tag, const gchar * value);
|
||||
static void frida_build_validity (GByteArray * out);
|
||||
static void frida_build_subject_public_key_info (GByteArray * out, const GByteArray * rsa_public_key);
|
||||
static void frida_build_signed_certificate (GByteArray * out, const GByteArray * tbs, const GByteArray * signature);
|
||||
static void frida_build_algorithm_identifier (GByteArray * out, const guint8 * oid, gsize oid_length);
|
||||
static void frida_build_pkcs8_private_key_info (GByteArray * out, const GByteArray * rsa_private_key);
|
||||
static gchar * frida_pem_encode (const gchar * type, const guint8 * der, gsize der_length);
|
||||
|
||||
static void frida_der_append_tlv (GByteArray * out, guint8 tag, const guint8 * value, gsize length);
|
||||
static void frida_der_append_length (GByteArray * out, gsize length);
|
||||
static void frida_der_append_integer_uint (GByteArray * out, guint64 value);
|
||||
static void frida_der_append_null (GByteArray * out);
|
||||
static void frida_der_append_oid (GByteArray * out, const guint8 * oid, gsize length);
|
||||
static void frida_der_append_utctime (GByteArray * out, time_t t);
|
||||
static void frida_der_append_bit_string (GByteArray * out, const guint8 * bytes, gsize length);
|
||||
|
||||
static const guint8 frida_oid_country[] = { 0x55, 0x04, 0x06 };
|
||||
static const guint8 frida_oid_organization[] = { 0x55, 0x04, 0x0a };
|
||||
static const guint8 frida_oid_common_name[] = { 0x55, 0x04, 0x03 };
|
||||
static const guint8 frida_oid_rsa_encryption[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 };
|
||||
static const guint8 frida_oid_sha256_with_rsa[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b };
|
||||
#else
|
||||
static gchar * frida_steal_bio_to_string (BIO ** bio);
|
||||
#endif
|
||||
|
||||
static int frida_on_connection_output (void * addr, void * buffer, size_t length, uint8_t tos, uint8_t set_df);
|
||||
static void frida_on_debug_printf (const char * format, ...);
|
||||
|
||||
static void frida_on_upcall (struct socket * sock, void * user_data, int flags);
|
||||
|
||||
#ifdef HAVE_GIOAPPLE
|
||||
|
||||
void
|
||||
_frida_generate_certificate (guint8 ** cert_der, gint * cert_der_length, gchar ** cert_pem, gchar ** key_pem)
|
||||
{
|
||||
SecKeyRef private_key;
|
||||
GByteArray * rsa_public_key, * rsa_private_key, * subject_public_key, * tbs, * signature, * certificate, * pkcs8;
|
||||
|
||||
*cert_der = NULL;
|
||||
*cert_der_length = 0;
|
||||
*cert_pem = NULL;
|
||||
*key_pem = NULL;
|
||||
|
||||
private_key = frida_rsa_keypair_generate ();
|
||||
|
||||
rsa_public_key = g_byte_array_new ();
|
||||
frida_rsa_export_public_key (private_key, rsa_public_key);
|
||||
|
||||
rsa_private_key = g_byte_array_new ();
|
||||
frida_rsa_export_private_key (private_key, rsa_private_key);
|
||||
|
||||
subject_public_key = g_byte_array_new ();
|
||||
frida_build_subject_public_key_info (subject_public_key, rsa_public_key);
|
||||
|
||||
tbs = g_byte_array_new ();
|
||||
frida_build_tbs_certificate (tbs, subject_public_key);
|
||||
|
||||
signature = g_byte_array_new ();
|
||||
frida_rsa_sign_sha256 (private_key, tbs->data, tbs->len, signature);
|
||||
|
||||
certificate = g_byte_array_new ();
|
||||
frida_build_signed_certificate (certificate, tbs, signature);
|
||||
|
||||
pkcs8 = g_byte_array_new ();
|
||||
frida_build_pkcs8_private_key_info (pkcs8, rsa_private_key);
|
||||
|
||||
*cert_der = g_memdup2 (certificate->data, certificate->len);
|
||||
*cert_der_length = certificate->len;
|
||||
*cert_pem = frida_pem_encode ("CERTIFICATE", certificate->data, certificate->len);
|
||||
*key_pem = frida_pem_encode ("PRIVATE KEY", pkcs8->data, pkcs8->len);
|
||||
|
||||
g_byte_array_unref (pkcs8);
|
||||
g_byte_array_unref (certificate);
|
||||
g_byte_array_unref (signature);
|
||||
g_byte_array_unref (tbs);
|
||||
g_byte_array_unref (subject_public_key);
|
||||
g_byte_array_unref (rsa_private_key);
|
||||
g_byte_array_unref (rsa_public_key);
|
||||
CFRelease (private_key);
|
||||
}
|
||||
|
||||
static SecKeyRef
|
||||
frida_rsa_keypair_generate (void)
|
||||
{
|
||||
SecKeyRef private_key;
|
||||
CFMutableDictionaryRef attrs;
|
||||
const int key_size_bits = 2048;
|
||||
CFNumberRef key_size;
|
||||
|
||||
attrs = CFDictionaryCreateMutable (kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
|
||||
key_size = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &key_size_bits);
|
||||
CFDictionarySetValue (attrs, kSecAttrKeyType, kSecAttrKeyTypeRSA);
|
||||
CFDictionarySetValue (attrs, kSecAttrKeySizeInBits, key_size);
|
||||
CFRelease (key_size);
|
||||
|
||||
private_key = SecKeyCreateRandomKey (attrs, NULL);
|
||||
g_assert (private_key != NULL);
|
||||
|
||||
CFRelease (attrs);
|
||||
|
||||
return private_key;
|
||||
}
|
||||
|
||||
static void
|
||||
frida_rsa_export_public_key (SecKeyRef private_key, GByteArray * out)
|
||||
{
|
||||
SecKeyRef public_key;
|
||||
CFDataRef data;
|
||||
|
||||
public_key = SecKeyCopyPublicKey (private_key);
|
||||
g_assert (public_key != NULL);
|
||||
|
||||
data = SecKeyCopyExternalRepresentation (public_key, NULL);
|
||||
g_assert (data != NULL);
|
||||
|
||||
g_byte_array_append (out, CFDataGetBytePtr (data), CFDataGetLength (data));
|
||||
|
||||
CFRelease (data);
|
||||
CFRelease (public_key);
|
||||
}
|
||||
|
||||
static void
|
||||
frida_rsa_export_private_key (SecKeyRef private_key, GByteArray * out)
|
||||
{
|
||||
CFDataRef data;
|
||||
|
||||
data = SecKeyCopyExternalRepresentation (private_key, NULL);
|
||||
g_assert (data != NULL);
|
||||
|
||||
g_byte_array_append (out, CFDataGetBytePtr (data), (guint) CFDataGetLength (data));
|
||||
|
||||
CFRelease (data);
|
||||
}
|
||||
|
||||
static void
|
||||
frida_rsa_sign_sha256 (SecKeyRef private_key, const guint8 * message, gsize message_length, GByteArray * out)
|
||||
{
|
||||
CFDataRef to_sign, signature;
|
||||
|
||||
to_sign = CFDataCreate (kCFAllocatorDefault, message, (CFIndex) message_length);
|
||||
|
||||
signature = SecKeyCreateSignature (private_key, kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA256, to_sign, NULL);
|
||||
g_assert (signature != NULL);
|
||||
|
||||
g_byte_array_append (out, CFDataGetBytePtr (signature), (guint) CFDataGetLength (signature));
|
||||
|
||||
CFRelease (signature);
|
||||
CFRelease (to_sign);
|
||||
}
|
||||
|
||||
static void
|
||||
frida_build_tbs_certificate (GByteArray * out, const GByteArray * subject_public_key)
|
||||
{
|
||||
GByteArray * inner = g_byte_array_new ();
|
||||
|
||||
frida_der_append_integer_uint (inner, 1);
|
||||
frida_build_algorithm_identifier (inner, frida_oid_sha256_with_rsa, sizeof (frida_oid_sha256_with_rsa));
|
||||
frida_build_name (inner);
|
||||
frida_build_validity (inner);
|
||||
frida_build_name (inner);
|
||||
g_byte_array_append (inner, subject_public_key->data, subject_public_key->len);
|
||||
|
||||
frida_der_append_tlv (out, 0x30, inner->data, inner->len);
|
||||
|
||||
g_byte_array_unref (inner);
|
||||
}
|
||||
|
||||
static void
|
||||
frida_build_name (GByteArray * out)
|
||||
{
|
||||
GByteArray * inner = g_byte_array_new ();
|
||||
|
||||
frida_build_rdn (inner, frida_oid_country, sizeof (frida_oid_country), 0x13, "CA");
|
||||
frida_build_rdn (inner, frida_oid_organization, sizeof (frida_oid_organization), 0x0c, "Frida");
|
||||
frida_build_rdn (inner, frida_oid_common_name, sizeof (frida_oid_common_name), 0x0c, "lolcathost");
|
||||
|
||||
frida_der_append_tlv (out, 0x30, inner->data, inner->len);
|
||||
|
||||
g_byte_array_unref (inner);
|
||||
}
|
||||
|
||||
static void
|
||||
frida_build_rdn (GByteArray * out, const guint8 * oid, gsize oid_length, guint8 value_tag, const gchar * value)
|
||||
{
|
||||
GByteArray * atv, * rdn;
|
||||
|
||||
atv = g_byte_array_new ();
|
||||
rdn = g_byte_array_new ();
|
||||
|
||||
frida_der_append_oid (atv, oid, oid_length);
|
||||
frida_der_append_tlv (atv, value_tag, (const guint8 *) value, strlen (value));
|
||||
frida_der_append_tlv (rdn, 0x30, atv->data, atv->len);
|
||||
frida_der_append_tlv (out, 0x31, rdn->data, rdn->len);
|
||||
|
||||
g_byte_array_unref (rdn);
|
||||
g_byte_array_unref (atv);
|
||||
}
|
||||
|
||||
static void
|
||||
frida_build_validity (GByteArray * out)
|
||||
{
|
||||
const time_t lifetime_seconds = 15780000;
|
||||
GByteArray * inner;
|
||||
time_t not_before, not_after;
|
||||
|
||||
inner = g_byte_array_new ();
|
||||
|
||||
not_before = time (NULL);
|
||||
not_after = not_before + lifetime_seconds;
|
||||
|
||||
frida_der_append_utctime (inner, not_before);
|
||||
frida_der_append_utctime (inner, not_after);
|
||||
|
||||
frida_der_append_tlv (out, 0x30, inner->data, inner->len);
|
||||
|
||||
g_byte_array_unref (inner);
|
||||
}
|
||||
|
||||
static void
|
||||
frida_build_subject_public_key_info (GByteArray * out, const GByteArray * rsa_public_key)
|
||||
{
|
||||
GByteArray * inner = g_byte_array_new ();
|
||||
|
||||
frida_build_algorithm_identifier (inner, frida_oid_rsa_encryption, sizeof (frida_oid_rsa_encryption));
|
||||
frida_der_append_bit_string (inner, rsa_public_key->data, rsa_public_key->len);
|
||||
|
||||
frida_der_append_tlv (out, 0x30, inner->data, inner->len);
|
||||
|
||||
g_byte_array_unref (inner);
|
||||
}
|
||||
|
||||
static void
|
||||
frida_build_signed_certificate (GByteArray * out, const GByteArray * tbs, const GByteArray * signature)
|
||||
{
|
||||
GByteArray * inner = g_byte_array_new ();
|
||||
|
||||
g_byte_array_append (inner, tbs->data, tbs->len);
|
||||
frida_build_algorithm_identifier (inner, frida_oid_sha256_with_rsa, sizeof (frida_oid_sha256_with_rsa));
|
||||
frida_der_append_bit_string (inner, signature->data, signature->len);
|
||||
|
||||
frida_der_append_tlv (out, 0x30, inner->data, inner->len);
|
||||
|
||||
g_byte_array_unref (inner);
|
||||
}
|
||||
|
||||
static void
|
||||
frida_build_algorithm_identifier (GByteArray * out, const guint8 * oid, gsize oid_length)
|
||||
{
|
||||
GByteArray * inner = g_byte_array_new ();
|
||||
|
||||
frida_der_append_oid (inner, oid, oid_length);
|
||||
frida_der_append_null (inner);
|
||||
|
||||
frida_der_append_tlv (out, 0x30, inner->data, inner->len);
|
||||
|
||||
g_byte_array_unref (inner);
|
||||
}
|
||||
|
||||
static void
|
||||
frida_build_pkcs8_private_key_info (GByteArray * out, const GByteArray * rsa_private_key)
|
||||
{
|
||||
GByteArray * inner = g_byte_array_new ();
|
||||
|
||||
frida_der_append_integer_uint (inner, 0);
|
||||
frida_build_algorithm_identifier (inner, frida_oid_rsa_encryption, sizeof (frida_oid_rsa_encryption));
|
||||
frida_der_append_tlv (inner, 0x04, rsa_private_key->data, rsa_private_key->len);
|
||||
|
||||
frida_der_append_tlv (out, 0x30, inner->data, inner->len);
|
||||
|
||||
g_byte_array_unref (inner);
|
||||
}
|
||||
|
||||
static gchar *
|
||||
frida_pem_encode (const gchar * type, const guint8 * der, gsize der_length)
|
||||
{
|
||||
GString * pem;
|
||||
gchar * base64;
|
||||
gsize length, i;
|
||||
|
||||
base64 = g_base64_encode (der, der_length);
|
||||
length = strlen (base64);
|
||||
|
||||
pem = g_string_sized_new (length + 128);
|
||||
|
||||
g_string_append_printf (pem, "-----BEGIN %s-----\n", type);
|
||||
|
||||
for (i = 0; i < length; i += 64)
|
||||
{
|
||||
gsize chunk = MIN (64, length - i);
|
||||
g_string_append_len (pem, base64 + i, chunk);
|
||||
g_string_append_c (pem, '\n');
|
||||
}
|
||||
|
||||
g_string_append_printf (pem, "-----END %s-----\n", type);
|
||||
|
||||
g_free (base64);
|
||||
|
||||
return g_string_free (pem, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
frida_der_append_tlv (GByteArray * out, guint8 tag, const guint8 * value, gsize length)
|
||||
{
|
||||
g_byte_array_append (out, &tag, 1);
|
||||
frida_der_append_length (out, length);
|
||||
if (length > 0)
|
||||
g_byte_array_append (out, value, length);
|
||||
}
|
||||
|
||||
static void
|
||||
frida_der_append_length (GByteArray * out, gsize length)
|
||||
{
|
||||
gint i;
|
||||
guint8 buf[9], header;
|
||||
|
||||
if (length < 0x80)
|
||||
{
|
||||
guint8 b = (guint8) length;
|
||||
g_byte_array_append (out, &b, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (length > 0)
|
||||
{
|
||||
buf[++i] = (guint8) (length & 0xff);
|
||||
length >>= 8;
|
||||
}
|
||||
header = (guint8) (0x80 | i);
|
||||
g_byte_array_append (out, &header, 1);
|
||||
while (i > 0)
|
||||
{
|
||||
g_byte_array_append (out, &buf[i], 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
frida_der_append_integer_uint (GByteArray * out, guint64 value)
|
||||
{
|
||||
gint i;
|
||||
guint8 buf[9];
|
||||
gint start;
|
||||
|
||||
for (i = 7; i >= 0; i--)
|
||||
{
|
||||
buf[i + 1] = (guint8) (value & 0xff);
|
||||
value >>= 8;
|
||||
}
|
||||
buf[0] = 0;
|
||||
|
||||
start = 0;
|
||||
while (start < 8 && buf[start] == 0 && (buf[start + 1] & 0x80) == 0)
|
||||
start++;
|
||||
|
||||
frida_der_append_tlv (out, 0x02, buf + start, (gsize) (9 - start));
|
||||
}
|
||||
|
||||
static void
|
||||
frida_der_append_null (GByteArray * out)
|
||||
{
|
||||
frida_der_append_tlv (out, 0x05, NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
frida_der_append_oid (GByteArray * out, const guint8 * oid, gsize length)
|
||||
{
|
||||
frida_der_append_tlv (out, 0x06, oid, length);
|
||||
}
|
||||
|
||||
static void
|
||||
frida_der_append_utctime (GByteArray * out, time_t t)
|
||||
{
|
||||
struct tm tm;
|
||||
gchar buf[16];
|
||||
|
||||
gmtime_r (&t, &tm);
|
||||
g_snprintf (buf, sizeof (buf), "%02d%02d%02d%02d%02d%02dZ",
|
||||
tm.tm_year % 100, tm.tm_mon + 1, tm.tm_mday,
|
||||
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||
|
||||
frida_der_append_tlv (out, 0x17, (const guint8 *) buf, strlen (buf));
|
||||
}
|
||||
|
||||
static void
|
||||
frida_der_append_bit_string (GByteArray * out, const guint8 * bytes, gsize length)
|
||||
{
|
||||
guint8 tag = 0x03;
|
||||
guint8 unused_bits = 0;
|
||||
|
||||
g_byte_array_append (out, &tag, 1);
|
||||
frida_der_append_length (out, length + 1);
|
||||
g_byte_array_append (out, &unused_bits, 1);
|
||||
if (length > 0)
|
||||
g_byte_array_append (out, bytes, length);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void
|
||||
_frida_generate_certificate (guint8 ** cert_der, gint * cert_der_length, gchar ** cert_pem, gchar ** key_pem)
|
||||
{
|
||||
@@ -90,6 +496,8 @@ frida_steal_bio_to_string (BIO ** bio)
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
_frida_sctp_connection_initialize_sctp_backend (void)
|
||||
{
|
||||
|
||||
@@ -128,6 +128,11 @@ namespace Frida {
|
||||
set;
|
||||
}
|
||||
|
||||
public WebRequestHandler? request_handler {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public EndpointParameters (string? address = null, uint16 port = 0, TlsCertificate? certificate = null,
|
||||
string? origin = null, AuthenticationService? auth_service = null, File? asset_root = null) {
|
||||
Object (
|
||||
@@ -141,6 +146,75 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public interface WebRequestHandler : Object {
|
||||
public abstract async WebResponse? handle_request (WebRequest request, Cancellable? cancellable) throws Error, IOError;
|
||||
}
|
||||
|
||||
public sealed class WebRequest : Object {
|
||||
public string method {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public string path {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public string? query_string {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public Bytes? body {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private Soup.MessageHeaders raw_headers;
|
||||
|
||||
internal WebRequest (string method, string path, string? query_string, Soup.MessageHeaders headers, Bytes? body) {
|
||||
Object (method: method, path: path, query_string: query_string, body: body);
|
||||
raw_headers = headers;
|
||||
}
|
||||
|
||||
public void foreach_header (WebRequestHeaderFunc func) {
|
||||
raw_headers.foreach ((name, val) => {
|
||||
func (name, val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void WebRequestHeaderFunc (string name, string val);
|
||||
|
||||
public sealed class WebResponse : Object {
|
||||
public uint status {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public Bytes body {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private Soup.MessageHeaders raw_headers = new Soup.MessageHeaders (RESPONSE);
|
||||
|
||||
public WebResponse (uint status, Bytes body) {
|
||||
Object (status: status, body: body);
|
||||
}
|
||||
|
||||
public void add_header (string name, string val) {
|
||||
raw_headers.append (name, val);
|
||||
}
|
||||
|
||||
internal void write_headers_to (Soup.MessageHeaders target) {
|
||||
raw_headers.foreach ((name, val) => {
|
||||
target.replace (name, val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async IOStream negotiate_connection (IOStream stream, WebServiceTransport transport, string host, string? origin,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
var input = (DataInputStream) Object.new (typeof (DataInputStream),
|
||||
@@ -468,14 +542,14 @@ namespace Frida {
|
||||
|
||||
server.add_websocket_handler ("/ws", endpoint_params.origin, null, on_websocket_opened);
|
||||
|
||||
if (endpoint_params.asset_root != null)
|
||||
server.add_handler (null, on_asset_request);
|
||||
if (endpoint_params.request_handler != null || endpoint_params.asset_root != null)
|
||||
server.add_handler (null, on_http_request);
|
||||
}
|
||||
|
||||
public void close () {
|
||||
io_cancellable.cancel ();
|
||||
|
||||
if (endpoint_params.asset_root != null)
|
||||
if (endpoint_params.request_handler != null || endpoint_params.asset_root != null)
|
||||
server.remove_handler ("/");
|
||||
server.remove_handler ("/ws");
|
||||
|
||||
@@ -575,20 +649,77 @@ namespace Frida {
|
||||
connections.remove (connection);
|
||||
}
|
||||
|
||||
private void on_asset_request (Soup.Server server, Soup.ServerMessage msg, string path,
|
||||
private void on_http_request (Soup.Server server, Soup.ServerMessage msg, string path,
|
||||
HashTable<string, string>? query) {
|
||||
msg.get_response_headers ().replace ("Server", "Frida/" + _version_string ());
|
||||
|
||||
unowned string method = msg.get_method ();
|
||||
if (method != "GET" && method != "HEAD") {
|
||||
msg.set_status (Soup.Status.METHOD_NOT_ALLOWED, null);
|
||||
msg.pause ();
|
||||
handle_http_request.begin (msg, path);
|
||||
}
|
||||
|
||||
private async void handle_http_request (Soup.ServerMessage msg, string path) {
|
||||
WebRequestHandler? handler = endpoint_params.request_handler;
|
||||
if (handler != null) {
|
||||
if (yield try_request_handler (handler, msg, path, io_cancellable))
|
||||
return;
|
||||
}
|
||||
|
||||
File? asset_root = endpoint_params.asset_root;
|
||||
if (asset_root != null) {
|
||||
unowned string method = msg.get_method ();
|
||||
if (method != "GET" && method != "HEAD") {
|
||||
msg.set_status (Soup.Status.METHOD_NOT_ALLOWED, null);
|
||||
msg.unpause ();
|
||||
return;
|
||||
}
|
||||
|
||||
File location = asset_root.resolve_relative_path (path.next_char ());
|
||||
yield handle_asset_request (path, location, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
File location = endpoint_params.asset_root.resolve_relative_path (path.next_char ());
|
||||
msg.set_status (Soup.Status.NOT_FOUND, null);
|
||||
msg.unpause ();
|
||||
}
|
||||
|
||||
msg.pause ();
|
||||
handle_asset_request.begin (path, location, msg);
|
||||
private static async bool try_request_handler (WebRequestHandler handler, Soup.ServerMessage msg, string path,
|
||||
Cancellable? cancellable) {
|
||||
var body = msg.get_request_body ();
|
||||
Bytes? request_body = (body.length != 0) ? body.flatten () : null;
|
||||
unowned Uri? uri = msg.get_uri ();
|
||||
string? query_string = (uri != null) ? uri.get_query () : null;
|
||||
var request = new WebRequest (msg.get_method (), path, query_string, msg.get_request_headers (),
|
||||
request_body);
|
||||
|
||||
WebResponse? response;
|
||||
try {
|
||||
response = yield handler.handle_request (request, cancellable);
|
||||
} catch (Error e) {
|
||||
msg.set_status (Soup.Status.INTERNAL_SERVER_ERROR, null);
|
||||
msg.set_response ("text/plain; charset=utf-8", Soup.MemoryUse.COPY, e.message.data);
|
||||
msg.unpause ();
|
||||
return true;
|
||||
} catch (IOError e) {
|
||||
msg.unpause ();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (response == null)
|
||||
return false;
|
||||
|
||||
apply_response (msg, response);
|
||||
msg.unpause ();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void apply_response (Soup.ServerMessage msg, WebResponse response) {
|
||||
msg.set_status (response.status, null);
|
||||
|
||||
response.write_headers_to (msg.get_response_headers ());
|
||||
|
||||
Bytes body = response.body;
|
||||
if (body.length > 0)
|
||||
msg.get_response_body ().append_bytes (body);
|
||||
}
|
||||
|
||||
private async void handle_asset_request (string path, File file, Soup.ServerMessage msg) {
|
||||
@@ -940,8 +1071,11 @@ namespace Frida {
|
||||
Memory.copy (buffer, recv_queue.data, n);
|
||||
recv_queue.remove_range (0, (uint) n);
|
||||
|
||||
if (recv_queue.len == 0)
|
||||
if (recv_queue.len == 0) {
|
||||
#if HAVE_SOUP_WEBSOCKET_BACKPRESSURE
|
||||
websocket.resume_input ();
|
||||
#endif
|
||||
}
|
||||
|
||||
update_pending_io ();
|
||||
} else {
|
||||
@@ -1009,7 +1143,9 @@ namespace Frida {
|
||||
private void on_message (int type, Bytes message) {
|
||||
with_state_lock (() => {
|
||||
recv_queue.append (message.get_data ());
|
||||
#if HAVE_SOUP_WEBSOCKET_BACKPRESSURE
|
||||
websocket.pause_input ();
|
||||
#endif
|
||||
update_pending_io ();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ namespace Frida {
|
||||
|
||||
public JsonObjectReader (string json) throws Error {
|
||||
try {
|
||||
reader = new Json.Reader (Json.from_string (json));
|
||||
reader = make_json_reader (json);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.INVALID_ARGUMENT ("%s", e.message);
|
||||
}
|
||||
@@ -404,4 +404,29 @@ namespace Frida {
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructs a Json.Reader for the given JSON string, working around the
|
||||
* json_node_copy() bug in unpatched json-glib (GNOME bugzilla #774684).
|
||||
*
|
||||
* Json.Reader's set_root() copies the supplied node via json_node_copy().
|
||||
* For object/array nodes that copy is a thin shell that shares the original
|
||||
* JsonObject/JsonArray via refcount, but the members inside the shared
|
||||
* container have their parent pointers set to the *original* root, not the
|
||||
* shell. If the original is then dropped, json_reader_end_member()'s walk
|
||||
* via json_node_get_parent() touches a freed node — which on builds with
|
||||
* glib checks enabled (e.g. Fedora) trips JSON_NODE_IS_VALID and aborts.
|
||||
*
|
||||
* We keep the original root alive by attaching it to the reader as object
|
||||
* data; the destroy notify drops our reference when the reader is finalized.
|
||||
*/
|
||||
public Json.Reader make_json_reader (string json) throws GLib.Error {
|
||||
return make_json_reader_from_node (Json.from_string (json));
|
||||
}
|
||||
|
||||
public Json.Reader make_json_reader_from_node (Json.Node root) {
|
||||
var reader = new Json.Reader (root);
|
||||
reader.set_data<Json.Node> ("frida-json-root", root);
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,9 @@
|
||||
# include <signal.h>
|
||||
#endif
|
||||
#include <gumjs/gumscriptbackend.h>
|
||||
#ifdef HAVE_GIOOPENSSL
|
||||
#if defined (HAVE_GIOAPPLE)
|
||||
# include <gioapple.h>
|
||||
#elif defined (HAVE_GIOOPENSSL)
|
||||
# include <gioopenssl.h>
|
||||
#endif
|
||||
|
||||
@@ -91,7 +93,9 @@ frida_gadget_environment_init (void)
|
||||
|
||||
g_thread_set_garbage_handler (_frida_gadget_on_pending_thread_garbage, NULL);
|
||||
|
||||
#ifdef HAVE_GIOOPENSSL
|
||||
#if defined (HAVE_GIOAPPLE)
|
||||
g_io_module_apple_register ();
|
||||
#elif defined (HAVE_GIOOPENSSL)
|
||||
g_io_module_openssl_register ();
|
||||
#endif
|
||||
|
||||
|
||||
@@ -4,10 +4,12 @@ endif
|
||||
subdir('base')
|
||||
subdir('netif')
|
||||
subdir('pipe')
|
||||
subdir('payload')
|
||||
if have_local_backend
|
||||
subdir('agent')
|
||||
endif
|
||||
if build_gadget
|
||||
subdir('gadget')
|
||||
if have_in_tree_payloads
|
||||
subdir('payload')
|
||||
if have_local_backend
|
||||
subdir('agent')
|
||||
endif
|
||||
if build_gadget
|
||||
subdir('gadget')
|
||||
endif
|
||||
endif
|
||||
@@ -224,15 +224,21 @@ helper_name = 'frida-helper' + exe_suffix
|
||||
agent_name = 'frida-agent' + shlib_suffix
|
||||
gadget_name = 'frida-gadget' + shlib_suffix
|
||||
|
||||
root_asset_dir = get_option('libdir') / 'frida'
|
||||
frida_libdir_name = f'frida-@api_version@'
|
||||
root_asset_dir = get_option('libdir') / frida_libdir_name
|
||||
if host_os_family == 'darwin'
|
||||
asset_dir = root_asset_dir
|
||||
default_asset_path_template = get_option('prefix') / get_option('libdir') / 'frida'
|
||||
default_asset_path_template = get_option('prefix') / get_option('libdir') / frida_libdir_name
|
||||
elif host_os_family == 'windows'
|
||||
asset_dir = root_asset_dir / host_abi
|
||||
asset_dir_modern = root_asset_dir / 'x86_64'
|
||||
asset_dir_legacy = root_asset_dir / 'x86'
|
||||
default_asset_path_template = get_option('prefix') / get_option('libdir') / frida_libdir_name / '<arch>'
|
||||
else
|
||||
asset_dir = root_asset_dir / host_cpu_mode
|
||||
asset_dir_modern = root_asset_dir / '64'
|
||||
asset_dir_legacy = root_asset_dir / '32'
|
||||
default_asset_path_template = get_option('prefix') / get_option('libdir') / 'frida' / '<arch>'
|
||||
default_asset_path_template = get_option('prefix') / get_option('libdir') / frida_libdir_name / '<arch>'
|
||||
endif
|
||||
|
||||
asset_path_template = get_option('asset_path_template')
|
||||
@@ -240,11 +246,21 @@ if asset_path_template == ''
|
||||
asset_path_template = default_asset_path_template
|
||||
endif
|
||||
|
||||
if get_option('assets') == 'embedded'
|
||||
is_shared = get_option('default_library') == 'shared'
|
||||
|
||||
assets_mode = get_option('assets')
|
||||
if assets_mode == 'auto'
|
||||
assets_mode = is_shared ? 'installed' : 'embedded'
|
||||
endif
|
||||
|
||||
cdata.set_quoted('FRIDA_LIBDIR_NAME', frida_libdir_name)
|
||||
|
||||
if assets_mode == 'embedded'
|
||||
cdata.set('HAVE_EMBEDDED_ASSETS', 1)
|
||||
vala_flags += '--define=HAVE_EMBEDDED_ASSETS'
|
||||
else
|
||||
cdata.set_quoted('FRIDA_HELPER_PATH', asset_path_template / helper_name)
|
||||
cdata.set_quoted('FRIDA_AGENT_PATH', asset_path_template / agent_name)
|
||||
cdata.set_quoted('FRIDA_HELPER_NAME', helper_name)
|
||||
cdata.set_quoted('FRIDA_AGENT_NAME', agent_name)
|
||||
endif
|
||||
|
||||
cdata.set('HAVE_' + host_os_family.to_upper(), 1)
|
||||
@@ -312,15 +328,21 @@ if get_option('b_sanitize') == 'address'
|
||||
cdata.set('HAVE_ASAN', 1)
|
||||
endif
|
||||
|
||||
have_in_tree_payloads = get_option('default_library') != 'shared'
|
||||
gum_dep = dependency('frida-gum-1.0', default_options: [
|
||||
'default_library=static',
|
||||
f'frida_version=@frida_version@',
|
||||
'gumjs=enabled',
|
||||
'gumjs=' + (have_in_tree_payloads ? 'enabled' : 'disabled'),
|
||||
'inspector=enabled',
|
||||
])
|
||||
gumjs_dep = dependency('frida-gumjs-1.0')
|
||||
gumjs_inspector_dep = dependency('frida-gumjs-inspector-1.0')
|
||||
if have_in_tree_payloads
|
||||
gumjs_dep = dependency('frida-gumjs-1.0', default_options: ['default_library=static'])
|
||||
endif
|
||||
gumjs_inspector_dep = dependency('frida-gumjs-inspector-1.0', default_options: ['default_library=static'])
|
||||
|
||||
glib_version_req = '>=2.74'
|
||||
glib_options = [
|
||||
'default_library=static',
|
||||
'cocoa=disabled',
|
||||
'selinux=disabled',
|
||||
'xattr=false',
|
||||
@@ -337,6 +359,7 @@ if optimize_for_prod
|
||||
endif
|
||||
|
||||
gee_options = [
|
||||
'default_library=static',
|
||||
'disable-introspection=true',
|
||||
]
|
||||
if optimize_for_prod
|
||||
@@ -344,16 +367,36 @@ if optimize_for_prod
|
||||
endif
|
||||
|
||||
glib_dep = dependency('glib-2.0', version: glib_version_req, default_options: glib_options)
|
||||
if glib_dep.type_name() == 'internal'
|
||||
have_shared_glib = get_option('default_library') != 'static'
|
||||
else
|
||||
glib_flavor_src = '''
|
||||
#include <glib.h>
|
||||
|
||||
#ifdef GLIB_STATIC_COMPILATION
|
||||
# error GLib is static
|
||||
#endif
|
||||
'''
|
||||
have_shared_glib = cc.compiles(glib_flavor_src, name: 'compiling with shared GLib', dependencies: [glib_dep])
|
||||
endif
|
||||
have_frida_glib = glib_dep.type_name() == 'internal' or cc.has_function('g_thread_set_callbacks', dependencies: [glib_dep])
|
||||
if have_frida_glib
|
||||
cdata.set('HAVE_FRIDA_GLIB', 1)
|
||||
vala_flags += '--define=HAVE_FRIDA_GLIB'
|
||||
endif
|
||||
gobject_dep = dependency('gobject-2.0')
|
||||
gmodule_dep = dependency('gmodule-2.0')
|
||||
gio_dep = dependency('gio-2.0')
|
||||
gee_dep = dependency('gee-0.8', default_options: gee_options)
|
||||
json_glib_dep = dependency('json-glib-1.0', version: '>=1.8', default_options: [
|
||||
'default_library=static',
|
||||
'introspection=disabled',
|
||||
'gtk_doc=disabled',
|
||||
'tests=false',
|
||||
])
|
||||
libsoup_dep = dependency('libsoup-3.0', default_options: [
|
||||
'default_library=static',
|
||||
'sqlite:default_library=static',
|
||||
'gssapi=disabled',
|
||||
'ntlm=disabled',
|
||||
'brotli=disabled',
|
||||
@@ -365,19 +408,48 @@ libsoup_dep = dependency('libsoup-3.0', default_options: [
|
||||
'tests=false',
|
||||
'sysprof=disabled',
|
||||
])
|
||||
brotlidec_dep = dependency('libbrotlidec')
|
||||
if libsoup_dep.type_name() == 'internal' \
|
||||
or cc.has_function('soup_websocket_connection_pause_input', dependencies: [libsoup_dep])
|
||||
vala_flags += '--define=HAVE_SOUP_WEBSOCKET_BACKPRESSURE'
|
||||
endif
|
||||
brotlidec_dep = dependency('libbrotlidec', default_options: ['default_library=static'])
|
||||
capstone_dep = dependency('capstone', default_options: ['default_library=static'])
|
||||
if capstone_dep.type_name() != 'internal' \
|
||||
and not cc.has_function('cs_arch_register_x86', dependencies: [capstone_dep])
|
||||
add_project_arguments(
|
||||
'-Dcs_arch_register_x86()=',
|
||||
'-Dcs_arch_register_arm()=',
|
||||
'-Dcs_arch_register_arm64()=',
|
||||
'-Dcs_arch_register_mips()=',
|
||||
language: ['c', 'objc'],
|
||||
)
|
||||
endif
|
||||
quickjs_dep = dependency('quickjs', required: get_option('barebone_backend'), default_options: [
|
||||
'default_library=static',
|
||||
'libc=false',
|
||||
'bignum=true',
|
||||
'atomics=disabled',
|
||||
'stack_check=disabled',
|
||||
])
|
||||
lzfse_dep = dependency('lzfse', required: get_option('barebone_backend'))
|
||||
lzfse_dep = dependency('lzfse', required: false, default_options: ['default_library=static'])
|
||||
lzfse_from_system = false
|
||||
if not lzfse_dep.found() and not meson.is_cross_build()
|
||||
if cc.has_header_symbol('lzfse.h', 'lzfse_decode_buffer')
|
||||
lzfse_lib = cc.find_library('lzfse', required: false)
|
||||
if lzfse_lib.found()
|
||||
lzfse_dep = declare_dependency(dependencies: lzfse_lib)
|
||||
lzfse_from_system = true
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
if not lzfse_dep.found()
|
||||
lzfse_dep = dependency('lzfse', required: get_option('barebone_backend'), default_options: ['default_library=static'])
|
||||
endif
|
||||
|
||||
native_glib_dep = dependency('glib-2.0', version: glib_version_req, native: true, default_options: glib_options)
|
||||
native_gio_dep = dependency('gio-2.0', native: true)
|
||||
native_gee_dep = dependency('gee-0.8', native: true, default_options: gee_options)
|
||||
native_brotlienc_dep = dependency('libbrotlienc', native: true)
|
||||
native_brotlienc_dep = dependency('libbrotlienc', native: true, default_options: ['default_library=static'])
|
||||
|
||||
if host_os_family == 'windows'
|
||||
gio_windows_dep = dependency('gio-windows-2.0')
|
||||
@@ -385,8 +457,10 @@ else
|
||||
gio_unix_dep = dependency('gio-unix-2.0')
|
||||
endif
|
||||
|
||||
if host_os_family == 'linux'
|
||||
libbpf_dep = dependency('libbpf')
|
||||
have_local_backend = get_option('local_backend').allowed()
|
||||
|
||||
if host_os_family == 'linux' and have_local_backend
|
||||
libbpf_dep = dependency('libbpf', default_options: ['default_library=static'])
|
||||
endif
|
||||
|
||||
if host_os == 'android'
|
||||
@@ -434,7 +508,6 @@ else
|
||||
codesign = ''
|
||||
endif
|
||||
|
||||
have_local_backend = get_option('local_backend').allowed()
|
||||
have_simmy_backend = get_option('simmy_backend') \
|
||||
.disable_auto_if(host_os != 'macos') \
|
||||
.require(host_os == 'macos', error_message: 'Simmy backend is only available on macOS') \
|
||||
@@ -464,11 +537,13 @@ if compiler_backend_opt.allowed()
|
||||
files('tools' / 'cgo' / 'detect.py'),
|
||||
go,
|
||||
host_os,
|
||||
host_os_family,
|
||||
host_abi,
|
||||
libc,
|
||||
exe_suffix,
|
||||
shlib_suffix,
|
||||
cc.get_id(),
|
||||
assets_mode,
|
||||
'>>>', cc.cmd_array(), '<<<',
|
||||
'>>>', ar, '<<<',
|
||||
'>>>', nm, '<<<',
|
||||
@@ -481,16 +556,27 @@ if compiler_backend_opt.allowed()
|
||||
status = lines[0]
|
||||
if status == 'ok'
|
||||
have_compiler_backend = true
|
||||
tokens = lines[1].split(' ')
|
||||
compiler_backend_mode = tokens[0]
|
||||
go_config = tokens[1]
|
||||
cdata.set('HAVE_COMPILER_BACKEND', 1)
|
||||
compiler_backend_mode = lines[1]
|
||||
go_config = lines[2]
|
||||
|
||||
if compiler_backend_mode == 'c-archive'
|
||||
vala_flags += '--define=COMPILER_BACKEND_EMBEDDED'
|
||||
vala_flags += '--define=COMPILER_BACKEND_LINKED'
|
||||
cdata.set('HAVE_COMPILER_BACKEND_LINKED', 1)
|
||||
elif compiler_backend_mode == 'c-shared'
|
||||
vala_flags += '--define=COMPILER_BACKEND_SHARED'
|
||||
if assets_mode == 'installed'
|
||||
vala_flags += '--define=COMPILER_BACKEND_INSTALLED_LIBRARY'
|
||||
cdata.set_quoted('FRIDA_COMPILER_BACKEND_NAME', 'frida-compiler-backend' + shlib_suffix)
|
||||
else
|
||||
vala_flags += '--define=COMPILER_BACKEND_EMBEDDED_LIBRARY'
|
||||
endif
|
||||
elif compiler_backend_mode == 'pie'
|
||||
vala_flags += '--define=COMPILER_BACKEND_EXECUTABLE'
|
||||
if assets_mode == 'installed'
|
||||
vala_flags += '--define=COMPILER_BACKEND_INSTALLED_EXECUTABLE'
|
||||
cdata.set_quoted('FRIDA_COMPILER_BACKEND_NAME', 'frida-compiler-backend' + exe_suffix)
|
||||
else
|
||||
vala_flags += '--define=COMPILER_BACKEND_EMBEDDED_EXECUTABLE'
|
||||
endif
|
||||
else
|
||||
error('Unsupported compiler backend mode: ' + compiler_backend_mode)
|
||||
endif
|
||||
@@ -561,8 +647,10 @@ endif
|
||||
gi_dep = dependency('gobject-introspection-1.0', required: false)
|
||||
if gi_dep.found()
|
||||
girdir = gi_dep.get_variable('girdir')
|
||||
typelibdir = gi_dep.get_variable('typelibdir')
|
||||
else
|
||||
girdir = get_option('prefix') / get_option('datadir') / 'gir-1.0'
|
||||
typelibdir = get_option('prefix') / get_option('libdir') / 'girepository-1.0'
|
||||
endif
|
||||
|
||||
vapidir = get_option('prefix') / get_option('datadir') / 'vala' / 'vapi'
|
||||
@@ -571,12 +659,15 @@ gum_vala_args = [
|
||||
'--pkg=frida-gum-1.0',
|
||||
'--vapidir=' + gum_dep.get_variable('frida_vapidir'),
|
||||
]
|
||||
gumjs_vala_args = ['--pkg=frida-gumjs-1.0', '--pkg=gio-2.0', gum_vala_args]
|
||||
gumjs_inspector_vala_args = ['--pkg=frida-gumjs-inspector-1.0', gum_vala_args]
|
||||
|
||||
have_v8 = gumjs_dep.get_variable('gumjs_v8') == 'enabled'
|
||||
if have_v8
|
||||
vala_flags += ['--define=HAVE_V8']
|
||||
if have_in_tree_payloads
|
||||
gumjs_vala_args = ['--pkg=frida-gumjs-1.0', '--pkg=gio-2.0', gum_vala_args]
|
||||
|
||||
have_v8 = gumjs_dep.get_variable('gumjs_v8') == 'enabled'
|
||||
if have_v8
|
||||
vala_flags += ['--define=HAVE_V8']
|
||||
endif
|
||||
endif
|
||||
|
||||
backend_deps_private = []
|
||||
@@ -584,6 +675,7 @@ backend_reqs_private = []
|
||||
backend_libs_private = []
|
||||
|
||||
openssl_options = [
|
||||
'default_library=static',
|
||||
'cli=disabled',
|
||||
]
|
||||
if host_os_family == 'windows' and cc.get_argument_syntax() != 'msvc'
|
||||
@@ -596,25 +688,48 @@ openssl_dep = dependency('openssl',
|
||||
)
|
||||
|
||||
if have_fruity_backend
|
||||
foreach dep : ['libnghttp2', 'libngtcp2', 'libngtcp2_crypto_quictls', 'libusb-1.0']
|
||||
backend_deps_private += dependency(dep)
|
||||
backend_reqs_private += dep
|
||||
endforeach
|
||||
backend_deps_private += dependency('libnghttp2', default_options: ['default_library=static'])
|
||||
backend_reqs_private += 'libnghttp2'
|
||||
|
||||
lwip_options = [
|
||||
'ipv4=disabled',
|
||||
'ipv6=enabled',
|
||||
'dns=disabled',
|
||||
'arp=disabled',
|
||||
'ethernet=enabled',
|
||||
]
|
||||
if optimize_for_prod
|
||||
lwip_options += 'lwip_debug=disabled'
|
||||
else
|
||||
lwip_options += 'lwip_debug=enabled'
|
||||
if host_os != 'macos'
|
||||
backend_deps_private += dependency('libngtcp2', default_options: ['default_library=static'])
|
||||
backend_reqs_private += 'libngtcp2'
|
||||
|
||||
ngtcp2_crypto_quictls_dep = dependency('libngtcp2_crypto_quictls', required: false, default_options: ['default_library=static'])
|
||||
if ngtcp2_crypto_quictls_dep.found()
|
||||
backend_deps_private += ngtcp2_crypto_quictls_dep
|
||||
backend_reqs_private += 'libngtcp2_crypto_quictls'
|
||||
else
|
||||
backend_deps_private += dependency('libngtcp2_crypto_ossl', default_options: ['default_library=static'])
|
||||
backend_reqs_private += 'libngtcp2_crypto_ossl'
|
||||
vala_flags += '--define=HAVE_NGTCP2_CRYPTO_OSSL'
|
||||
endif
|
||||
|
||||
if have_frida_glib
|
||||
libusb_dep = dependency('libusb-1.0', default_options: ['default_library=static'])
|
||||
else
|
||||
subproject('libusb', default_options: ['default_library=static'])
|
||||
libusb_dep = dependency('libusb-1.0', default_options: ['default_library=static'])
|
||||
endif
|
||||
backend_deps_private += libusb_dep
|
||||
backend_reqs_private += 'libusb-1.0'
|
||||
|
||||
lwip_options = [
|
||||
'default_library=static',
|
||||
'ipv4=disabled',
|
||||
'ipv6=enabled',
|
||||
'dns=disabled',
|
||||
'arp=disabled',
|
||||
'ethernet=enabled',
|
||||
]
|
||||
if optimize_for_prod
|
||||
lwip_options += 'lwip_debug=disabled'
|
||||
else
|
||||
lwip_options += 'lwip_debug=enabled'
|
||||
endif
|
||||
backend_deps_private += dependency('lwip', default_options: lwip_options)
|
||||
backend_reqs_private += 'lwip'
|
||||
endif
|
||||
backend_deps_private += dependency('lwip', default_options: lwip_options)
|
||||
backend_reqs_private += 'lwip'
|
||||
endif
|
||||
|
||||
if quickjs_dep.found()
|
||||
@@ -624,10 +739,14 @@ endif
|
||||
|
||||
if lzfse_dep.found()
|
||||
backend_deps_private += lzfse_dep
|
||||
backend_reqs_private += 'lzfse'
|
||||
if lzfse_from_system
|
||||
backend_libs_private += '-llzfse'
|
||||
else
|
||||
backend_reqs_private += 'lzfse'
|
||||
endif
|
||||
endif
|
||||
|
||||
if host_os_family == 'linux'
|
||||
if host_os_family == 'linux' and have_local_backend
|
||||
backend_deps_private += libbpf_dep
|
||||
backend_reqs_private += 'libbpf'
|
||||
endif
|
||||
@@ -638,43 +757,87 @@ endif
|
||||
|
||||
if host_os == 'android'
|
||||
termux_elf_cleaner = find_program('termux-elf-cleaner', native: true)
|
||||
libselinux_dep = dependency('libselinux', version: '>=3.0')
|
||||
libsepol_dep = dependency('libsepol', version: '>=3.0')
|
||||
libselinux_dep = dependency('libselinux', version: '>=3.0', default_options: ['default_library=static'])
|
||||
libsepol_dep = dependency('libsepol', version: '>=3.0', default_options: ['default_library=static'])
|
||||
backend_deps_private += [libselinux_dep, libsepol_dep]
|
||||
backend_reqs_private += ['libselinux', 'libsepol']
|
||||
else
|
||||
termux_elf_cleaner = []
|
||||
endif
|
||||
|
||||
tls_provider_dep = dependency('gioopenssl', required: get_option('connectivity'), default_options: [
|
||||
'gnutls=disabled',
|
||||
'openssl=enabled',
|
||||
'libproxy=disabled',
|
||||
'gnome_proxy=disabled',
|
||||
'tests=false',
|
||||
])
|
||||
tls_provider_vala_args = []
|
||||
if tls_provider_dep.found()
|
||||
tls_provider_vala_args += [
|
||||
'--pkg=gioopenssl',
|
||||
'--vapidir=' + tls_provider_dep.get_variable('vapidir'),
|
||||
]
|
||||
cdata.set('HAVE_GIOOPENSSL', 1)
|
||||
vala_flags += ['--define=HAVE_GIOOPENSSL']
|
||||
backend_deps_private += tls_provider_dep
|
||||
backend_reqs_private += 'gioopenssl'
|
||||
if have_shared_glib
|
||||
tls_provider_dep = gio_dep
|
||||
else
|
||||
use_apple_tls = false
|
||||
if host_os_family == 'darwin'
|
||||
apple_tls_deployment_check = '''
|
||||
#include <Availability.h>
|
||||
|
||||
#if defined (__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 150000
|
||||
# error macOS deployment target below 15.0
|
||||
#endif
|
||||
#if defined (__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 180000
|
||||
# error iOS deployment target below 18.0
|
||||
#endif
|
||||
#if defined (__TV_OS_VERSION_MIN_REQUIRED) && __TV_OS_VERSION_MIN_REQUIRED < 180000
|
||||
# error tvOS deployment target below 18.0
|
||||
#endif
|
||||
#if defined (__WATCH_OS_VERSION_MIN_REQUIRED) && __WATCH_OS_VERSION_MIN_REQUIRED < 110000
|
||||
# error watchOS deployment target below 11.0
|
||||
#endif
|
||||
'''
|
||||
use_apple_tls = cc.compiles(apple_tls_deployment_check, name: 'Apple TLS backend deployment target')
|
||||
endif
|
||||
if use_apple_tls
|
||||
tls_provider_name = 'gioapple'
|
||||
tls_provider_define_name = 'HAVE_GIOAPPLE'
|
||||
tls_provider_backend_options = [
|
||||
'gnutls=disabled',
|
||||
'openssl=disabled',
|
||||
'apple=enabled',
|
||||
]
|
||||
else
|
||||
tls_provider_name = 'gioopenssl'
|
||||
tls_provider_define_name = 'HAVE_GIOOPENSSL'
|
||||
tls_provider_backend_options = [
|
||||
'gnutls=disabled',
|
||||
'openssl=enabled',
|
||||
'apple=disabled',
|
||||
]
|
||||
endif
|
||||
tls_provider_dep = dependency(tls_provider_name,
|
||||
required: get_option('connectivity'),
|
||||
default_options: tls_provider_backend_options + [
|
||||
'libproxy=disabled',
|
||||
'gnome_proxy=disabled',
|
||||
'tests=false',
|
||||
],
|
||||
)
|
||||
if tls_provider_dep.found()
|
||||
tls_provider_vala_args += [
|
||||
'--pkg=' + tls_provider_name,
|
||||
'--vapidir=' + tls_provider_dep.get_variable('vapidir'),
|
||||
]
|
||||
cdata.set(tls_provider_define_name, 1)
|
||||
vala_flags += ['--define=' + tls_provider_define_name]
|
||||
backend_deps_private += tls_provider_dep
|
||||
backend_reqs_private += tls_provider_name
|
||||
endif
|
||||
endif
|
||||
|
||||
nice_dep = dependency('nice', required: get_option('connectivity'), default_options: [
|
||||
'default_library=static',
|
||||
'gupnp=disabled',
|
||||
'gstreamer=disabled',
|
||||
'crypto-library=openssl',
|
||||
'crypto-library=auto',
|
||||
'examples=disabled',
|
||||
'tests=disabled',
|
||||
'introspection=disabled',
|
||||
])
|
||||
if nice_dep.found()
|
||||
usrsctp_dep = dependency('usrsctp', default_options: [
|
||||
'default_library=static',
|
||||
'sctp_inet=false',
|
||||
'sctp_inet6=false',
|
||||
'sctp_build_programs=false',
|
||||
@@ -682,8 +845,12 @@ if nice_dep.found()
|
||||
|
||||
cdata.set('HAVE_NICE', 1)
|
||||
vala_flags += ['--define=HAVE_NICE']
|
||||
backend_deps_private += [nice_dep, openssl_dep, usrsctp_dep]
|
||||
backend_reqs_private += ['nice', 'openssl', 'usrsctp']
|
||||
backend_deps_private += [nice_dep, usrsctp_dep]
|
||||
backend_reqs_private += ['nice', 'usrsctp']
|
||||
if host_os != 'macos'
|
||||
backend_deps_private += openssl_dep
|
||||
backend_reqs_private += 'openssl'
|
||||
endif
|
||||
|
||||
if host_os_family in ['darwin', 'freebsd', 'qnx']
|
||||
cdata.set('HAVE_SCONN_LEN', 1)
|
||||
@@ -693,10 +860,10 @@ else
|
||||
endif
|
||||
|
||||
if host_os_family == 'windows'
|
||||
backend_libs_private += ['-lsetupapi']
|
||||
backend_libs_private += ['-lsetupapi', '-lshlwapi']
|
||||
endif
|
||||
if host_os_family == 'darwin'
|
||||
backend_libs_private += ['-Wl,-framework,Foundation', '-lbsm']
|
||||
backend_libs_private += ['-Wl,-framework,Foundation', '-Wl,-framework,CoreFoundation', '-lbsm']
|
||||
endif
|
||||
if host_os == 'macos'
|
||||
backend_libs_private += ['-Wl,-framework,AppKit']
|
||||
@@ -809,11 +976,20 @@ if cc.get_id() == 'gcc' and cc.compiles('#if __GNUC__ >= 5\n#error NOTNEEDED\n#e
|
||||
add_project_arguments('-w', language: 'c')
|
||||
endif
|
||||
|
||||
vapidir = meson.current_source_dir() / 'vapi'
|
||||
local_vapidir = meson.current_source_dir() / 'vapi'
|
||||
vala_flags += [
|
||||
'--vapidir=' + vapidir,
|
||||
'--vapidir=' + local_vapidir,
|
||||
'--pkg', 'config',
|
||||
]
|
||||
if gee_dep.type_name() not in ['internal', 'cmake']
|
||||
gee_prefix = gee_dep.get_variable('prefix', default_value: '')
|
||||
if gee_prefix != ''
|
||||
gee_vapidir = gee_prefix / 'share' / 'vala' / 'vapi'
|
||||
if fs.is_dir(gee_vapidir)
|
||||
vala_flags += '--vapidir=' + gee_vapidir
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
vala_flags += '--define=' + host_os_family.to_upper()
|
||||
if host_os != host_os_family
|
||||
@@ -825,13 +1001,22 @@ if host_abi != host_arch
|
||||
endif
|
||||
vala_flags += '--define=' + host_machine.endian().to_upper() + '_ENDIAN'
|
||||
add_project_arguments(vala_flags, language: 'vala')
|
||||
add_project_arguments('-I' + vapidir, language: 'c')
|
||||
add_project_arguments('-I' + local_vapidir, language: 'c')
|
||||
|
||||
native_vala_flags = [
|
||||
'--vapidir=' + meson.current_source_dir() / 'vapi',
|
||||
'--vapidir=' + local_vapidir,
|
||||
]
|
||||
if native_gee_dep.type_name() not in ['internal', 'cmake']
|
||||
native_gee_prefix = native_gee_dep.get_variable('prefix', default_value: '')
|
||||
if native_gee_prefix != ''
|
||||
native_gee_vapidir = native_gee_prefix / 'share' / 'vala' / 'vapi'
|
||||
if fs.is_dir(native_gee_vapidir)
|
||||
native_vala_flags += '--vapidir=' + native_gee_vapidir
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
add_project_arguments(native_vala_flags, language: 'vala', native: true)
|
||||
add_project_arguments('-I' + vapidir, language: 'c', native: true)
|
||||
add_project_arguments('-I' + local_vapidir, language: 'c', native: true)
|
||||
|
||||
subdir('compat')
|
||||
subdir('tools')
|
||||
@@ -866,7 +1051,7 @@ summary(
|
||||
summary(
|
||||
{
|
||||
'compat': compat_summary,
|
||||
'assets': get_option('assets'),
|
||||
'assets': assets_mode,
|
||||
'mapper': have_mapper,
|
||||
'connectivity': tls_provider_dep.found(),
|
||||
'gadget': build_gadget,
|
||||
|
||||
@@ -48,8 +48,8 @@ option('compiler_backend',
|
||||
|
||||
option('assets',
|
||||
type: 'combo',
|
||||
choices: ['embedded', 'installed'],
|
||||
value: 'embedded',
|
||||
choices: ['auto', 'embedded', 'installed'],
|
||||
value: 'auto',
|
||||
description: 'Whether assets such as frida-agent should be embedded vs installed and loaded at runtime'
|
||||
)
|
||||
|
||||
|
||||
@@ -39,7 +39,9 @@ namespace Frida.Portal {
|
||||
};
|
||||
|
||||
private static int main (string[] args) {
|
||||
#if HAVE_GIOOPENSSL
|
||||
#if HAVE_GIOAPPLE
|
||||
GIOApple.register ();
|
||||
#elif HAVE_GIOOPENSSL
|
||||
GIOOpenSSL.register ();
|
||||
#endif
|
||||
|
||||
|
||||
@@ -29,15 +29,20 @@ if host_os == 'macos'
|
||||
extra_link_depends += [server_plist]
|
||||
endif
|
||||
|
||||
server_deps = [core_dep]
|
||||
if host_os_family == 'darwin'
|
||||
server_deps += dependency('appleframeworks', modules: ['CoreFoundation'])
|
||||
endif
|
||||
|
||||
raw_server = executable('frida-server-raw', server_sources,
|
||||
vala_args: [core_vala_args, '--pkg=posix'],
|
||||
link_args: extra_link_args,
|
||||
link_depends: extra_link_depends,
|
||||
dependencies: [core_dep],
|
||||
dependencies: server_deps,
|
||||
)
|
||||
|
||||
server_name = 'frida-server' + exe_suffix
|
||||
need_universal_server = get_option('assets') == 'installed' and server_compat != ''
|
||||
need_universal_server = assets_mode == 'installed' and server_compat != ''
|
||||
|
||||
server = custom_target('frida-server',
|
||||
input: [raw_server, 'frida-server.xcent'],
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Frida {
|
||||
if (response.is_null ())
|
||||
return HostApplicationInfo.empty ();
|
||||
|
||||
Json.Reader reader = new Json.Reader (response);
|
||||
Json.Reader reader = make_json_reader_from_node (response);
|
||||
|
||||
string? identifier = null;
|
||||
string? name = null;
|
||||
@@ -75,7 +75,7 @@ namespace Frida {
|
||||
|
||||
Json.Node response = yield transport.request (stanza.get_root (), cancellable);
|
||||
|
||||
Json.Reader reader = new Json.Reader (response);
|
||||
Json.Reader reader = make_json_reader_from_node (response);
|
||||
|
||||
int num_apps = reader.count_elements ();
|
||||
if (num_apps == -1)
|
||||
@@ -141,7 +141,7 @@ namespace Frida {
|
||||
|
||||
Json.Node response = yield transport.request (stanza.get_root (), cancellable);
|
||||
|
||||
Json.Reader reader = new Json.Reader (response);
|
||||
Json.Reader reader = make_json_reader_from_node (response);
|
||||
|
||||
int num_processes = reader.count_elements ();
|
||||
if (num_processes == -1)
|
||||
@@ -195,7 +195,7 @@ namespace Frida {
|
||||
|
||||
Json.Node response = yield transport.request (stanza.get_root (), cancellable);
|
||||
|
||||
var r = new Json.Reader (response);
|
||||
var r = make_json_reader_from_node (response);
|
||||
parse_envelope_or_throw (r);
|
||||
|
||||
r.read_element (1);
|
||||
@@ -265,7 +265,7 @@ namespace Frida {
|
||||
|
||||
Json.Node response = yield transport.request (stanza.get_root (), cancellable);
|
||||
|
||||
var r = new Json.Reader (response);
|
||||
var r = make_json_reader_from_node (response);
|
||||
parse_envelope_or_throw (r);
|
||||
}
|
||||
|
||||
@@ -279,7 +279,7 @@ namespace Frida {
|
||||
|
||||
Json.Node response = yield transport.request (stanza.get_root (), cancellable);
|
||||
|
||||
var r = new Json.Reader (response);
|
||||
var r = make_json_reader_from_node (response);
|
||||
parse_envelope_or_throw (r);
|
||||
|
||||
r.read_element (1);
|
||||
@@ -292,7 +292,7 @@ namespace Frida {
|
||||
private async void request_ok (Json.Builder stanza, Cancellable? cancellable) throws Error, IOError {
|
||||
Json.Node response = yield transport.request (stanza.get_root (), cancellable);
|
||||
|
||||
var r = new Json.Reader (response);
|
||||
var r = make_json_reader_from_node (response);
|
||||
parse_envelope_or_throw (r);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,57 +23,49 @@ CORE_TAG_METHOD = f"{{{CORE_NAMESPACE}}}method"
|
||||
|
||||
OBJECT_TYPE_PATTERN = re.compile(r"\bpublic\s+(sealed )?(class|interface)\s+(\w+)\b")
|
||||
|
||||
TOPLEVEL_NAMES = [
|
||||
"frida.vala",
|
||||
"package-manager.vala",
|
||||
"control-service.vala",
|
||||
"portal-service.vala",
|
||||
"file-monitor.vala",
|
||||
Path("compiler") / "compiler.vala",
|
||||
]
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Generate refined Frida API definitions")
|
||||
parser.add_argument('--output', dest='output_type', choices=['bundle', 'header', 'gir', 'vapi', 'vapi-stamp'], default='bundle')
|
||||
parser.add_argument('frida_version', metavar='frida-version', type=str)
|
||||
parser.add_argument('frida_major_version', metavar='frida-major-version', type=str)
|
||||
parser.add_argument('frida_minor_version', metavar='frida-minor-version', type=str)
|
||||
parser.add_argument('frida_micro_version', metavar='frida-micro-version', type=str)
|
||||
parser.add_argument('frida_nano_version', metavar='frida-nano-version', type=str)
|
||||
parser.add_argument('api_version', metavar='api-version', type=str)
|
||||
parser.add_argument('core_header', metavar='/path/to/frida-core.h', type=argparse.FileType('r', encoding='utf-8'))
|
||||
parser.add_argument('core_gir', metavar='/path/to/Frida-x.y.gir', type=argparse.FileType('r', encoding='utf-8'))
|
||||
parser.add_argument('core_vapi', metavar='/path/to/frida-core.vapi', type=argparse.FileType('r', encoding='utf-8'))
|
||||
parser.add_argument('base_header', metavar='/path/to/frida-base.h', type=argparse.FileType('r', encoding='utf-8'))
|
||||
parser.add_argument('base_gir', metavar='/path/to/FridaBase-x.y.gir', type=argparse.FileType('r', encoding='utf-8'))
|
||||
parser.add_argument('base_vapi', metavar='/path/to/frida-base.vapi', type=argparse.FileType('r', encoding='utf-8'))
|
||||
parser.add_argument('--output', dest='output_type', choices=['bundle', 'header', 'gir', 'vapi', 'vapi-stamp', 'symbol-maps'], default='bundle')
|
||||
parser.add_argument('output_dir', metavar='/output/dir')
|
||||
parser.add_argument('extra_args', nargs='*')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
output_type = args.output_type
|
||||
frida_version = args.frida_version
|
||||
frida_version_components = (
|
||||
args.frida_major_version,
|
||||
args.frida_minor_version,
|
||||
args.frida_micro_version,
|
||||
args.frida_nano_version,
|
||||
)
|
||||
api_version = args.api_version
|
||||
core_header = args.core_header.read()
|
||||
core_gir = args.core_gir.read()
|
||||
core_vapi = args.core_vapi.read()
|
||||
base_header = args.base_header.read()
|
||||
base_gir = args.base_gir.read()
|
||||
base_vapi = args.base_vapi.read()
|
||||
output_dir = Path(args.output_dir)
|
||||
|
||||
if output_type == 'symbol-maps':
|
||||
emit_symbol_maps_from_source(output_dir)
|
||||
return
|
||||
|
||||
extra = args.extra_args
|
||||
frida_version = extra[0]
|
||||
frida_version_components = tuple(extra[1:5])
|
||||
api_version = extra[5]
|
||||
|
||||
if output_type == 'vapi-stamp':
|
||||
(output_dir / f"frida-core-{api_version}.vapi.stamp").write_bytes(b"")
|
||||
return
|
||||
|
||||
toplevel_names = [
|
||||
"frida.vala",
|
||||
"package-manager.vala",
|
||||
"control-service.vala",
|
||||
"portal-service.vala",
|
||||
"file-monitor.vala",
|
||||
Path("compiler") / "compiler.vala",
|
||||
]
|
||||
core_header = Path(extra[6]).read_text(encoding='utf-8')
|
||||
core_gir = Path(extra[7]).read_text(encoding='utf-8')
|
||||
core_vapi = Path(extra[8]).read_text(encoding='utf-8')
|
||||
base_header = Path(extra[9]).read_text(encoding='utf-8')
|
||||
base_gir = Path(extra[10]).read_text(encoding='utf-8')
|
||||
base_vapi = Path(extra[11]).read_text(encoding='utf-8')
|
||||
|
||||
toplevel_sources = []
|
||||
src_dir = Path(__file__).parent.parent.resolve()
|
||||
for name in toplevel_names:
|
||||
for name in TOPLEVEL_NAMES:
|
||||
toplevel_sources.append((src_dir / name).read_text(encoding='utf-8'))
|
||||
toplevel_code = "\n".join(toplevel_sources)
|
||||
|
||||
@@ -249,6 +241,29 @@ def emit_gir(api: ApiSpec, core_gir: str, base_gir: str, output_dir: Path) -> st
|
||||
merge_and_transform_elements("interface", object_type_names)
|
||||
merge_and_transform_elements("enumeration", enum_type_names | error_type_names)
|
||||
|
||||
for source_root in [core_root, base_root]:
|
||||
for record in source_root.findall(".//record", GIR_NAMESPACES):
|
||||
owner = record.get(f"{{{GLIB_NAMESPACE}}}is-gtype-struct-for")
|
||||
if owner is not None and owner in object_type_names:
|
||||
merged_namespace.append(record)
|
||||
|
||||
known_names = object_type_names | enum_type_names | error_type_names
|
||||
referenced_names = set()
|
||||
for elem in merged_namespace.iter():
|
||||
type_name = elem.get("name")
|
||||
if type_name is None:
|
||||
continue
|
||||
for prefix in ("Frida.", "FridaBase."):
|
||||
if type_name.startswith(prefix):
|
||||
referenced_names.add(type_name[len(prefix):])
|
||||
break
|
||||
needed_names = referenced_names - known_names
|
||||
for source_root in [core_root, base_root]:
|
||||
source_namespace = source_root.find("namespace", GIR_NAMESPACES)
|
||||
for callback in source_namespace.findall("callback", GIR_NAMESPACES):
|
||||
if callback.get("name") in needed_names:
|
||||
merged_namespace.append(callback)
|
||||
|
||||
ET.indent(merged_root, space=" ")
|
||||
result = ET.tostring(merged_root,
|
||||
encoding="unicode",
|
||||
@@ -304,6 +319,17 @@ def emit_vapi(api, output_dir):
|
||||
output_deps_file.write("gobject-2.0\n")
|
||||
output_deps_file.write("gio-2.0\n")
|
||||
|
||||
def emit_symbol_maps_from_source(output_dir: Path):
|
||||
with OutputFile(output_dir / 'frida-core.version') as f:
|
||||
f.write("{\n")
|
||||
f.write(" global:\n")
|
||||
f.write(" frida_*;\n")
|
||||
f.write(" _frida_*;\n")
|
||||
f.write("\n")
|
||||
f.write(" local:\n")
|
||||
f.write(" *;\n")
|
||||
f.write("};\n")
|
||||
|
||||
def parse_api(frida_version, frida_version_components, api_version, toplevel_code, core_header, core_vapi, base_header, base_vapi):
|
||||
all_headers = core_header + "\n" + base_header
|
||||
|
||||
@@ -325,6 +351,9 @@ def parse_api(frida_version, frida_version_components, api_version, toplevel_cod
|
||||
"EndpointParameters": "PortalService",
|
||||
"AuthenticationService": "EndpointParameters",
|
||||
"StaticAuthenticationService": "AuthenticationService",
|
||||
"WebRequestHandler": "StaticAuthenticationService",
|
||||
"WebRequest": "WebRequestHandler",
|
||||
"WebResponse": "WebRequest",
|
||||
}
|
||||
internal_type_prefixes = [
|
||||
"AgentMessageKind",
|
||||
@@ -419,7 +448,7 @@ def parse_api(frida_version, frida_version_components, api_version, toplevel_cod
|
||||
object_type.c_method_prototypes.append(method_cprototype)
|
||||
elif method_name == 'get_type':
|
||||
object_type.c_get_type = method_cprototype.replace("G_GNUC_CONST ;", "G_GNUC_CONST;")
|
||||
for d in re.finditer(r"^typedef.+?\(\*(" + object_type.c_name + r".+?)\) \(.+\);$", core_header, re.MULTILINE):
|
||||
for d in re.finditer(r"^typedef.+?\(\*(" + object_type.c_name + r".+?)\) \(.+\);$", all_headers, re.MULTILINE):
|
||||
delegate_cname = d.group(1)
|
||||
if delegate_cname not in seen_cdelegates:
|
||||
seen_cdelegates.add(delegate_cname)
|
||||
@@ -510,6 +539,8 @@ def function_is_public(name):
|
||||
"generate_certificate",
|
||||
"get_dbus_context",
|
||||
"invalidate_dbus_context",
|
||||
"make_json_reader",
|
||||
"make_json_reader_from_node",
|
||||
"make_parameters_dict",
|
||||
"compute_system_parameters",
|
||||
"parse_control_address",
|
||||
|
||||
@@ -3,6 +3,7 @@ fs = import('fs')
|
||||
generate_api = [python, files('generate.py')]
|
||||
|
||||
generate_api_args = [
|
||||
meson.current_build_dir(),
|
||||
frida_version,
|
||||
frida_major_version.to_string(),
|
||||
frida_minor_version.to_string(),
|
||||
@@ -15,7 +16,6 @@ generate_api_args = [
|
||||
base_build_dir / 'frida-base.h',
|
||||
base_build_dir / base_gir_name,
|
||||
base_build_dir / f'frida-base-@[email protected]',
|
||||
meson.current_build_dir(),
|
||||
]
|
||||
|
||||
core_api = custom_target('frida-core-api',
|
||||
@@ -44,6 +44,24 @@ glib_gir = fs.copyfile('GLib-2.0.gir', install: install_glib_girs, install_dir:
|
||||
gobject_gir = fs.copyfile('GObject-2.0.gir', install: install_glib_girs, install_dir: girdir)
|
||||
gio_gir = fs.copyfile('Gio-2.0.gir', install: install_glib_girs, install_dir: girdir)
|
||||
|
||||
g_ir_compiler = find_program('g-ir-compiler', required: false)
|
||||
if g_ir_compiler.found()
|
||||
custom_target('frida-core-typelib',
|
||||
command: [
|
||||
g_ir_compiler,
|
||||
'--output', '@OUTPUT@',
|
||||
'--includedir=' + girdir,
|
||||
'--includedir=' + meson.current_source_dir(),
|
||||
'@INPUT@',
|
||||
],
|
||||
input: core_public_gir,
|
||||
output: f'Frida-@[email protected]',
|
||||
depends: core,
|
||||
install: true,
|
||||
install_dir: typelibdir,
|
||||
)
|
||||
endif
|
||||
|
||||
core_public_vapi_stamp = custom_target('frida-core-vapi-stamp',
|
||||
input: core_public_vapi,
|
||||
output: f'frida-core-@[email protected]',
|
||||
@@ -54,22 +72,26 @@ core_public_vapi_stamp = custom_target('frida-core-vapi-stamp',
|
||||
],
|
||||
)
|
||||
|
||||
core_public_lib = custom_target('frida-core-library',
|
||||
input: [core] + internal_libs,
|
||||
output: f'libfrida-core-@[email protected]',
|
||||
command: [
|
||||
python,
|
||||
files('merge.py'),
|
||||
lib,
|
||||
libtool,
|
||||
ar,
|
||||
meson.current_build_dir(),
|
||||
'@OUTPUT@',
|
||||
'@INPUT@'
|
||||
],
|
||||
install: true,
|
||||
install_dir: get_option('libdir'),
|
||||
)
|
||||
if is_shared
|
||||
core_public_lib = core
|
||||
else
|
||||
core_public_lib = custom_target('frida-core-library',
|
||||
input: [core] + internal_libs,
|
||||
output: f'libfrida-core-@[email protected]',
|
||||
command: [
|
||||
python,
|
||||
files('merge.py'),
|
||||
lib,
|
||||
libtool,
|
||||
ar,
|
||||
meson.current_build_dir(),
|
||||
'@OUTPUT@',
|
||||
'@INPUT@'
|
||||
],
|
||||
install: true,
|
||||
install_dir: get_option('libdir'),
|
||||
)
|
||||
endif
|
||||
|
||||
core_public_extra_headers = []
|
||||
if host_os == 'android'
|
||||
@@ -96,29 +118,43 @@ installed_variables = {
|
||||
'frida_vapidir': vapidir,
|
||||
}
|
||||
|
||||
core_public_dep = declare_dependency(
|
||||
sources: [core_public_h, core_public_gir, glib_gir, gobject_gir, gio_gir, core_public_vapi_stamp, core_public_extra_headers],
|
||||
include_directories: include_directories('.'),
|
||||
link_args: backend_libs_private,
|
||||
link_with: core_public_lib,
|
||||
dependencies: core_deps,
|
||||
variables: uninstalled_variables,
|
||||
)
|
||||
core_public_dep_kwargs = {
|
||||
'sources': [core_public_h, core_public_gir, glib_gir, gobject_gir, gio_gir, core_public_vapi_stamp] + core_public_extra_headers,
|
||||
'include_directories': include_directories('.'),
|
||||
'link_with': core_public_lib,
|
||||
'dependencies': core_deps,
|
||||
'variables': uninstalled_variables,
|
||||
}
|
||||
if not is_shared
|
||||
core_public_dep_kwargs += {
|
||||
'link_args': backend_libs_private,
|
||||
}
|
||||
endif
|
||||
core_public_dep = declare_dependency(kwargs: core_public_dep_kwargs)
|
||||
|
||||
pkg = import('pkgconfig')
|
||||
pkg.generate(
|
||||
filebase: f'frida-core-@api_version@',
|
||||
name: 'Frida Core Library',
|
||||
version: frida_version,
|
||||
description: 'Instrumentation toolkit for developers, reverse-engineers, and security researchers',
|
||||
requires: ['glib-2.0', 'gobject-2.0', 'gio-2.0', 'json-glib-1.0'],
|
||||
requires_private: ['gmodule-2.0', 'gee-0.8', 'libsoup-3.0', 'frida-gum-1.0', 'frida-gumjs-inspector-1.0', 'libbrotlidec'] + backend_reqs_private,
|
||||
subdirs: f'frida-@api_version@',
|
||||
libraries: core_public_lib,
|
||||
libraries_private: backend_libs_private,
|
||||
extra_cflags: ['-I${frida_includedir}'],
|
||||
variables: installed_variables,
|
||||
uninstalled_variables: uninstalled_variables,
|
||||
)
|
||||
pkg_kwargs = {
|
||||
'filebase': f'frida-core-@api_version@',
|
||||
'name': 'Frida Core Library',
|
||||
'version': frida_version,
|
||||
'description': 'Instrumentation toolkit for developers, reverse-engineers, and security researchers',
|
||||
'requires': ['glib-2.0', 'gobject-2.0', 'gio-2.0', 'json-glib-1.0'],
|
||||
'subdirs': f'frida-@api_version@',
|
||||
'extra_cflags': ['-I${frida_includedir}'],
|
||||
'variables': installed_variables,
|
||||
'uninstalled_variables': uninstalled_variables,
|
||||
}
|
||||
if is_shared
|
||||
pkg_kwargs += {
|
||||
'libraries': ['-L${libdir}', f'-lfrida-core-@api_version@'],
|
||||
}
|
||||
else
|
||||
pkg_kwargs += {
|
||||
'libraries': core_public_lib,
|
||||
'requires_private': ['gmodule-2.0', 'gee-0.8', 'libsoup-3.0', 'frida-gum-1.0', 'frida-gumjs-inspector-1.0', 'libbrotlidec'] + backend_reqs_private,
|
||||
'libraries_private': backend_libs_private,
|
||||
}
|
||||
endif
|
||||
pkg.generate(kwargs: pkg_kwargs)
|
||||
|
||||
meson.override_dependency(f'frida-core-@api_version@', core_public_dep)
|
||||
@@ -14,15 +14,21 @@ namespace Frida {
|
||||
|
||||
public sealed class BareboneHostSessionProvider : Object, HostSessionProvider {
|
||||
public string id {
|
||||
get { return "barebone"; }
|
||||
get {
|
||||
return "barebone";
|
||||
}
|
||||
}
|
||||
|
||||
public string name {
|
||||
get { return "GDB Remote Stub"; }
|
||||
get {
|
||||
return "GDB Remote Stub";
|
||||
}
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
get { return null; }
|
||||
get {
|
||||
return _icon;
|
||||
}
|
||||
}
|
||||
|
||||
public HostSessionProviderKind kind {
|
||||
@@ -31,8 +37,13 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private static Variant _icon;
|
||||
private BareboneHostSession? host_session;
|
||||
|
||||
static construct {
|
||||
_icon = make_provider_icon (Frida.Data.Icons.get_barebone_png_blob ().data);
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
if (host_session != null) {
|
||||
yield host_session.close (cancellable);
|
||||
|
||||
@@ -401,7 +401,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private void handle_message (Json.Node message) throws Error {
|
||||
var r = new Json.Reader (message);
|
||||
var r = make_json_reader_from_node (message);
|
||||
|
||||
bool is_response = r.read_member ("id");
|
||||
uint id = 0;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef _MSC_VER
|
||||
#if defined (_MSC_VER) && defined (HAVE_COMPILER_BACKEND_LINKED)
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
__frida_compiler_backend_*
|
||||
@@ -1,4 +1,3 @@
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
@@ -10,7 +9,7 @@ from typing import List
|
||||
|
||||
|
||||
def main(argv):
|
||||
go_config = json.loads(base64.b64decode(argv[1]).decode("utf-8"))
|
||||
go_config = json.loads(Path(argv[1]).read_text(encoding="utf-8"))
|
||||
output_dir, priv_dir, go, npm, *inputs = [Path(d).resolve() for d in argv[2:]]
|
||||
|
||||
try:
|
||||
@@ -74,8 +73,12 @@ def build_backend(
|
||||
extra_go_args = config["extra_go_args"].copy()
|
||||
|
||||
if mode == "c-shared":
|
||||
version_script = priv_dir / "backend.version"
|
||||
extra_go_args.append(f"-ldflags=-linkmode=external -extldflags=-Wl,--version-script={version_script}")
|
||||
if config["os_family"] == "darwin":
|
||||
symbols_list = priv_dir / "backend.symbols"
|
||||
extra_go_args.append(f"-ldflags=-linkmode=external -extldflags=-Wl,-exported_symbols_list,{symbols_list}")
|
||||
elif config["os_family"] != "windows":
|
||||
version_script = priv_dir / "backend.version"
|
||||
extra_go_args.append(f"-ldflags=-linkmode=external -extldflags=-Wl,--version-script={version_script}")
|
||||
|
||||
run(
|
||||
go,
|
||||
|
||||
@@ -235,12 +235,24 @@ namespace Frida {
|
||||
namespace CompilerBackend {
|
||||
private void init () {
|
||||
#if HAVE_COMPILER_BACKEND
|
||||
#if COMPILER_BACKEND_EMBEDDED
|
||||
#if COMPILER_BACKEND_LINKED
|
||||
_init_go_runtime ();
|
||||
build = (BuildFunc) _build;
|
||||
watch = (WatchFunc) _watch;
|
||||
WatchSession.dispose = (WatchSession.DisposeFunc) WatchSession._dispose;
|
||||
#elif COMPILER_BACKEND_SHARED
|
||||
#elif COMPILER_BACKEND_INSTALLED_LIBRARY
|
||||
Module? backend = null;
|
||||
try {
|
||||
backend = new Module (Frida.compiler_backend_path, LOCAL);
|
||||
} catch (ModuleError e) {
|
||||
return;
|
||||
}
|
||||
backend.make_resident ();
|
||||
|
||||
build = resolve_symbol (backend, "_frida_compiler_backend_build");
|
||||
watch = resolve_symbol (backend, "_frida_compiler_backend_watch");
|
||||
WatchSession.dispose = resolve_symbol (backend, "_frida_compiler_backend_watch_session_dispose");
|
||||
#elif COMPILER_BACKEND_EMBEDDED_LIBRARY
|
||||
unowned uint8[] backend_so = Frida.Data.Compiler.get_frida_compiler_backend_so_blob ().data;
|
||||
|
||||
Module? backend = null;
|
||||
@@ -272,11 +284,12 @@ namespace Frida {
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
backend.make_resident ();
|
||||
|
||||
build = resolve_symbol (backend, "_frida_compiler_backend_build");
|
||||
watch = resolve_symbol (backend, "_frida_compiler_backend_watch");
|
||||
WatchSession.dispose = resolve_symbol (backend, "_frida_compiler_backend_watch_session_dispose");
|
||||
#elif COMPILER_BACKEND_EXECUTABLE
|
||||
#elif COMPILER_BACKEND_EMBEDDED_EXECUTABLE || COMPILER_BACKEND_INSTALLED_EXECUTABLE
|
||||
backend_process = new BackendProcess ();
|
||||
|
||||
build = executable_build;
|
||||
@@ -287,8 +300,15 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private void check_available () throws Error {
|
||||
if (build == null)
|
||||
if (build == null) {
|
||||
#if COMPILER_BACKEND_INSTALLED_LIBRARY || COMPILER_BACKEND_INSTALLED_EXECUTABLE
|
||||
throw new Error.NOT_SUPPORTED (
|
||||
"Compiler backend plugin not installed; expected at: %s",
|
||||
Frida.compiler_backend_path);
|
||||
#else
|
||||
throw new Error.NOT_SUPPORTED ("Compiler backend disabled at build-time");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private BuildFunc? build;
|
||||
@@ -305,7 +325,7 @@ namespace Frida {
|
||||
string platform, string[] externals, StartingFunc on_starting, FinishedFunc on_finished,
|
||||
OutputFunc on_output, DiagnosticFunc on_diagnostic, owned WatchReadyFunc on_ready);
|
||||
|
||||
#if COMPILER_BACKEND_EMBEDDED
|
||||
#if COMPILER_BACKEND_LINKED
|
||||
private extern void _init_go_runtime ();
|
||||
private extern void _build ();
|
||||
private extern void _watch ();
|
||||
@@ -317,7 +337,7 @@ namespace Frida {
|
||||
|
||||
private DisposeFunc? dispose;
|
||||
|
||||
#if COMPILER_BACKEND_EMBEDDED
|
||||
#if COMPILER_BACKEND_LINKED
|
||||
private extern void _dispose ();
|
||||
#endif
|
||||
}
|
||||
@@ -330,7 +350,7 @@ namespace Frida {
|
||||
private delegate void DiagnosticFunc (owned string category, int code, owned string? path, int line, int character,
|
||||
owned string text);
|
||||
|
||||
#if HAVE_COMPILER_BACKEND && COMPILER_BACKEND_SHARED
|
||||
#if HAVE_COMPILER_BACKEND && (COMPILER_BACKEND_EMBEDDED_LIBRARY || COMPILER_BACKEND_INSTALLED_LIBRARY)
|
||||
private T resolve_symbol<T> (Module m, string name) {
|
||||
void * address;
|
||||
if (!m.symbol (name, out address))
|
||||
@@ -339,7 +359,7 @@ namespace Frida {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if COMPILER_BACKEND_EXECUTABLE
|
||||
#if COMPILER_BACKEND_EMBEDDED_EXECUTABLE || COMPILER_BACKEND_INSTALLED_EXECUTABLE
|
||||
private BackendProcess? backend_process;
|
||||
|
||||
private static void executable_build (string project_root, string entrypoint, OutputFormat output_format,
|
||||
@@ -393,7 +413,13 @@ namespace Frida {
|
||||
if (process != null)
|
||||
return;
|
||||
|
||||
#if COMPILER_BACKEND_INSTALLED_EXECUTABLE
|
||||
unowned string path = Frida.compiler_backend_path;
|
||||
bool unlink_after = false;
|
||||
#else
|
||||
string path = extract_backend_executable ();
|
||||
bool unlink_after = true;
|
||||
#endif
|
||||
try {
|
||||
var p = new Subprocess (STDIN_PIPE | STDOUT_PIPE | STDERR_PIPE, path);
|
||||
process = p;
|
||||
@@ -408,10 +434,12 @@ namespace Frida {
|
||||
process_incoming_messages.begin ();
|
||||
process_stderr_stream.begin (errput);
|
||||
} finally {
|
||||
FileUtils.unlink (path);
|
||||
if (unlink_after)
|
||||
FileUtils.unlink (path);
|
||||
}
|
||||
}
|
||||
|
||||
#if COMPILER_BACKEND_EMBEDDED_EXECUTABLE
|
||||
private static string extract_backend_executable () throws GLib.Error {
|
||||
unowned uint8[] blob = Frida.Data.Compiler.get_frida_compiler_backend_blob ().data;
|
||||
|
||||
@@ -425,6 +453,7 @@ namespace Frida {
|
||||
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
||||
private void handle_process_failure (string message) {
|
||||
io_cancellable.cancel ();
|
||||
@@ -714,15 +743,13 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private void handle_message (string json) throws Error {
|
||||
Json.Node root;
|
||||
Json.Reader reader;
|
||||
try {
|
||||
root = Json.from_string (json);
|
||||
reader = make_json_reader (json);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
|
||||
var reader = new Json.Reader (root);
|
||||
|
||||
reader.read_member ("type");
|
||||
unowned string? type = reader.get_string_value ();
|
||||
if (type == null)
|
||||
|
||||
@@ -29,6 +29,7 @@ if have_compiler_backend
|
||||
'backend.go',
|
||||
'protocol_types.go',
|
||||
'backend.version',
|
||||
'backend.symbols',
|
||||
'tscompiler.go',
|
||||
'tsconfig.go',
|
||||
'shims.go',
|
||||
@@ -36,6 +37,7 @@ if have_compiler_backend
|
||||
'package-lock.json',
|
||||
],
|
||||
output: output_files,
|
||||
depend_files: go_config,
|
||||
command: [
|
||||
python,
|
||||
files('build-backend.py'),
|
||||
@@ -58,6 +60,8 @@ if have_compiler_backend
|
||||
input: compiler_backend[0],
|
||||
output: processed_filename,
|
||||
command: post_process + ['shared-library', identity, 'elf-cleaner:off'],
|
||||
install: assets_mode == 'installed',
|
||||
install_dir: get_option('libdir') / frida_libdir_name / 'plugins',
|
||||
)
|
||||
elif compiler_backend_mode == 'pie'
|
||||
processed_filename = 'frida-compiler-backend' + exe_suffix
|
||||
@@ -65,27 +69,33 @@ if have_compiler_backend
|
||||
input: compiler_backend[0],
|
||||
output: processed_filename,
|
||||
command: post_process + ['executable', processed_filename],
|
||||
install: assets_mode == 'installed',
|
||||
install_dir: get_option('libdir') / frida_libdir_name / 'plugins',
|
||||
)
|
||||
endif
|
||||
|
||||
compiler_data = custom_target('frida-data-compiler',
|
||||
input: [
|
||||
'compiler.resources',
|
||||
compiler_backend_binary,
|
||||
],
|
||||
output: [
|
||||
'frida-data-compiler.vapi',
|
||||
'frida-data-compiler.h',
|
||||
'frida-data-compiler.c',
|
||||
'frida-data-compiler-blob' + resource_blob_extension,
|
||||
],
|
||||
command: [
|
||||
resource_compiler_cmd_array,
|
||||
'-c', '@INPUT0@',
|
||||
'-o', meson.current_build_dir() / 'frida-data-compiler',
|
||||
'@INPUT1@',
|
||||
],
|
||||
)
|
||||
backend_sources += compiler_data
|
||||
if assets_mode == 'installed'
|
||||
core_extra_link_depends += compiler_backend_binary
|
||||
else
|
||||
compiler_data = custom_target('frida-data-compiler',
|
||||
input: [
|
||||
'compiler.resources',
|
||||
compiler_backend_binary,
|
||||
],
|
||||
output: [
|
||||
'frida-data-compiler.vapi',
|
||||
'frida-data-compiler.h',
|
||||
'frida-data-compiler.c',
|
||||
'frida-data-compiler-blob' + resource_blob_extension,
|
||||
],
|
||||
command: [
|
||||
resource_compiler_cmd_array,
|
||||
'-c', '@INPUT0@',
|
||||
'-o', meson.current_build_dir() / 'frida-data-compiler',
|
||||
'@INPUT1@',
|
||||
],
|
||||
)
|
||||
backend_sources += compiler_data
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@@ -44,7 +44,7 @@ namespace Frida {
|
||||
#if HAVE_EMBEDDED_ASSETS
|
||||
return null;
|
||||
#else
|
||||
unowned string path = Config.FRIDA_AGENT_PATH;
|
||||
unowned string path = Frida.agent_path;
|
||||
# if IOS || TVOS
|
||||
unowned string? cryptex_path = Environment.get_variable ("CRYPTEX_MOUNT_PATH");
|
||||
if (cryptex_path != null)
|
||||
@@ -1053,7 +1053,7 @@ namespace Frida {
|
||||
|
||||
var parameters = make_parameters_dict ();
|
||||
try {
|
||||
var header = new Json.Reader (Json.from_string (raw_header));
|
||||
var header = make_json_reader (raw_header);
|
||||
foreach (string member in header.list_members ()) {
|
||||
header.read_member (member);
|
||||
|
||||
|
||||
@@ -512,7 +512,7 @@ namespace Frida {
|
||||
tempdir));
|
||||
FileUtils.chmod (helper.path, 0700);
|
||||
#else
|
||||
helper = new InstalledHelperFile.for_path (Config.FRIDA_HELPER_PATH);
|
||||
helper = new InstalledHelperFile.for_path (Frida.helper_path);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -23,5 +23,5 @@ custom_target('core-devkit',
|
||||
meson.current_build_dir(),
|
||||
] + devkit_options,
|
||||
install: true,
|
||||
install_dir: get_option('libdir') / 'frida' / 'devkits' / 'core'
|
||||
install_dir: get_option('libdir') / frida_libdir_name / 'devkits' / 'core'
|
||||
)
|
||||
@@ -118,13 +118,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
construct {
|
||||
var builder = new VariantBuilder (VariantType.VARDICT);
|
||||
builder.add ("{sv}", "format", new Variant.string ("rgba"));
|
||||
builder.add ("{sv}", "width", new Variant.uint16 (16));
|
||||
builder.add ("{sv}", "height", new Variant.uint16 (16));
|
||||
var image = new Bytes (Base64.decode ("AAAAAAAAAAAAAAAAAAAAAP///0DS4pz/////MP///0D///9A////MNflqP////9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8QzN6Q/7vTa/+vy1L/r8tS/7vTa//O4JXv////EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1eSkz6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/9XkpM8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8vfjcKrIRf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+qyEX/8PXeYAAAAAAAAAAAAAAAAAAAAAAAAAAA////QNLinL+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/97qt6////9AAAAAAAAAAAAAAAAA2eatv7vTa//G2oP/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf/M3pD/u9Nr/9nmrb8AAAAAAAAAANLinP+kxDn/u9Nr/6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+kxDn/u9Nr/6TEOf/S4pz/AAAAAAAAAADS4pz/pMQ5/7vTa/+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/7vTa/+kxDn/0uKc/wAAAAAAAAAA0uKc/6TEOf+702v/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+702v/pMQ5/9LinP8AAAAAAAAAANLinP+kxDn/u9Nr/6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+kxDn/u9Nr/6TEOf/S4pz/AAAAAAAAAADO4JXvpMQ5/8DWd/+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/8DWd/+kxDn/zuCV7wAAAAAAAAAA7fPXUNLinIDl7sbfpMQ5/6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf/l7sbf0uKcgO3z11AAAAAAAAAAAAAAAAAAAAAA8PXeYMDWd/+qyEX/pMQ5/6/LUv+vy1L/pMQ5/6rIRf/A1nf/7fPXUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu9Nr/6TEOf/C2Hu/wth7v6TEOf+702v/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALvTa/+kxDn/wth7v8LYe7+kxDn/u9Nr/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADc6LPPu9Nr/+HrvY/h672Pu9Nr/9nmrb8AAAAAAAAAAAAAAAAAAAAAAAAAAA=="));
|
||||
builder.add ("{sv}", "image", Variant.new_from_data (new VariantType ("ay"), image.get_data (), true, image));
|
||||
_icon = builder.end ();
|
||||
_icon = make_provider_icon (Frida.Data.Icons.get_droidy_png_blob ().data);
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
@@ -135,7 +129,18 @@ namespace Frida {
|
||||
if (host_session != null)
|
||||
throw new Error.INVALID_ARGUMENT ("Already created");
|
||||
|
||||
host_session = new DroidyHostSession (device_details, this);
|
||||
string control_endpoint = ("tcp:%" + uint16.FORMAT_MODIFIER + "u").printf (DEFAULT_CONTROL_PORT);
|
||||
|
||||
if (options != null) {
|
||||
Value? control_endpoint_val = options.map["control-endpoint"];
|
||||
if (control_endpoint_val != null) {
|
||||
if (!control_endpoint_val.holds (typeof (string)))
|
||||
throw new Error.INVALID_ARGUMENT ("The control-endpoint option must be a string");
|
||||
control_endpoint = control_endpoint_val.get_string ();
|
||||
}
|
||||
}
|
||||
|
||||
host_session = new DroidyHostSession (device_details, this, control_endpoint);
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
|
||||
return host_session;
|
||||
@@ -231,6 +236,11 @@ namespace Frida {
|
||||
construct;
|
||||
}
|
||||
|
||||
public string control_endpoint {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private Promise<AndroidHelperClient>? helper_client_request;
|
||||
private Droidy.ShellSession? helper_shell;
|
||||
|
||||
@@ -258,10 +268,12 @@ namespace Frida {
|
||||
private const double MIN_SERVER_CHECK_INTERVAL = 5.0;
|
||||
private const string GADGET_APP_ID = "re.frida.Gadget";
|
||||
|
||||
public DroidyHostSession (Droidy.DeviceDetails device_details, HostChannelProvider channel_provider) {
|
||||
public DroidyHostSession (Droidy.DeviceDetails device_details, HostChannelProvider channel_provider,
|
||||
string control_endpoint) {
|
||||
Object (
|
||||
device_details: device_details,
|
||||
channel_provider: channel_provider
|
||||
channel_provider: channel_provider,
|
||||
control_endpoint: control_endpoint
|
||||
);
|
||||
}
|
||||
|
||||
@@ -838,9 +850,7 @@ namespace Frida {
|
||||
|
||||
DBusConnection? connection = null;
|
||||
try {
|
||||
var stream = yield channel_provider.open_channel (
|
||||
("tcp:%" + uint16.FORMAT_MODIFIER + "u").printf (DEFAULT_CONTROL_PORT),
|
||||
cancellable);
|
||||
var stream = yield channel_provider.open_channel (control_endpoint, cancellable);
|
||||
|
||||
WebServiceTransport transport = PLAIN;
|
||||
string? origin = null;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include "frida-core.h"
|
||||
|
||||
#include <gum/gum.h>
|
||||
#ifdef HAVE_GIOOPENSSL
|
||||
#if defined (HAVE_GIOAPPLE)
|
||||
# include <gioapple.h>
|
||||
#elif defined (HAVE_GIOOPENSSL)
|
||||
# include <gioopenssl.h>
|
||||
#endif
|
||||
|
||||
@@ -10,6 +12,11 @@ static GThread * main_thread;
|
||||
static GMainLoop * main_loop;
|
||||
static GMainContext * main_context;
|
||||
|
||||
#ifndef HAVE_EMBEDDED_ASSETS
|
||||
extern void _frida_init_asset_paths (void);
|
||||
extern void _frida_deinit_asset_paths (void);
|
||||
#endif
|
||||
|
||||
static gpointer run_main_loop (gpointer data);
|
||||
static gboolean dummy_callback (gpointer data);
|
||||
static gboolean stop_main_loop (gpointer data);
|
||||
@@ -25,18 +32,27 @@ frida_init_with_runtime (FridaRuntime rt)
|
||||
{
|
||||
static gsize frida_initialized = FALSE;
|
||||
|
||||
runtime = rt;
|
||||
|
||||
#ifdef HAVE_FRIDA_GLIB
|
||||
g_thread_set_garbage_handler (frida_on_pending_garbage, NULL);
|
||||
glib_init ();
|
||||
#endif
|
||||
|
||||
if (g_once_init_enter (&frida_initialized))
|
||||
{
|
||||
runtime = rt;
|
||||
|
||||
#ifdef HAVE_FRIDA_GLIB
|
||||
gio_init ();
|
||||
#endif
|
||||
gum_init ();
|
||||
#ifndef HAVE_EMBEDDED_ASSETS
|
||||
_frida_init_asset_paths ();
|
||||
#endif
|
||||
frida_error_quark (); /* Initialize early so GDBus will pick it up */
|
||||
|
||||
#ifdef HAVE_GIOOPENSSL
|
||||
#if defined (HAVE_GIOAPPLE)
|
||||
g_io_module_apple_register ();
|
||||
#elif defined (HAVE_GIOOPENSSL)
|
||||
g_io_module_openssl_register ();
|
||||
#endif
|
||||
|
||||
@@ -109,12 +125,20 @@ frida_deinit (void)
|
||||
frida_invalidate_dbus_context ();
|
||||
|
||||
gum_shutdown ();
|
||||
#ifdef HAVE_FRIDA_GLIB
|
||||
gio_shutdown ();
|
||||
glib_shutdown ();
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_EMBEDDED_ASSETS
|
||||
_frida_deinit_asset_paths ();
|
||||
#endif
|
||||
|
||||
gum_deinit ();
|
||||
#ifdef HAVE_FRIDA_GLIB
|
||||
gio_deinit ();
|
||||
glib_deinit ();
|
||||
#endif
|
||||
}
|
||||
|
||||
GMainContext *
|
||||
|
||||
@@ -581,6 +581,40 @@ namespace Frida {
|
||||
return close_request != null;
|
||||
}
|
||||
|
||||
public void override_option (string name, Variant val) throws Error {
|
||||
Value v;
|
||||
switch (val.classify ()) {
|
||||
case BOOLEAN:
|
||||
v = Value (typeof (bool));
|
||||
v.set_boolean (val.get_boolean ());
|
||||
break;
|
||||
case INT64:
|
||||
v = Value (typeof (int64));
|
||||
v.set_int64 (val.get_int64 ());
|
||||
break;
|
||||
case UINT64:
|
||||
v = Value (typeof (uint64));
|
||||
v.set_uint64 (val.get_uint64 ());
|
||||
break;
|
||||
case DOUBLE:
|
||||
v = Value (typeof (double));
|
||||
v.set_double (val.get_double ());
|
||||
break;
|
||||
case STRING:
|
||||
v = Value (typeof (string));
|
||||
v.set_string (val.get_string ());
|
||||
break;
|
||||
default:
|
||||
throw new Error.INVALID_ARGUMENT ("Unsupported option type");
|
||||
}
|
||||
|
||||
lock (host_session_options) {
|
||||
if (host_session_options == null)
|
||||
host_session_options = new HostSessionOptions ();
|
||||
host_session_options.map[name] = v;
|
||||
}
|
||||
}
|
||||
|
||||
public async HashTable<string, Variant> query_system_parameters (Cancellable? cancellable = null) throws Error, IOError {
|
||||
check_open ();
|
||||
|
||||
@@ -1332,7 +1366,11 @@ namespace Frida {
|
||||
host_session_request = new Promise<HostSession> ();
|
||||
|
||||
try {
|
||||
var session = yield provider.create (manager, host_session_options, cancellable);
|
||||
HostSessionOptions? opts;
|
||||
lock (host_session_options)
|
||||
opts = (host_session_options != null) ? host_session_options.copy () : null;
|
||||
|
||||
var session = yield provider.create (manager, opts, cancellable);
|
||||
attach_host_session (session);
|
||||
|
||||
current_host_session = session;
|
||||
@@ -3583,11 +3621,39 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
#if !HAVE_EMBEDDED_ASSETS
|
||||
internal string helper_path;
|
||||
internal string agent_path;
|
||||
#if COMPILER_BACKEND_INSTALLED_LIBRARY || COMPILER_BACKEND_INSTALLED_EXECUTABLE
|
||||
internal string compiler_backend_path;
|
||||
#endif
|
||||
|
||||
public void _init_asset_paths () {
|
||||
var location = AssetLocation.detect ();
|
||||
helper_path = location.derive_asset_path ("<arch>", Config.FRIDA_HELPER_NAME);
|
||||
agent_path = location.derive_asset_path ("<arch>", Config.FRIDA_AGENT_NAME);
|
||||
#if COMPILER_BACKEND_INSTALLED_LIBRARY || COMPILER_BACKEND_INSTALLED_EXECUTABLE
|
||||
compiler_backend_path = location.derive_plugin_path (Config.FRIDA_COMPILER_BACKEND_NAME);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void _deinit_asset_paths () {
|
||||
helper_path = null;
|
||||
agent_path = null;
|
||||
#if COMPILER_BACKEND_INSTALLED_LIBRARY || COMPILER_BACKEND_INSTALLED_EXECUTABLE
|
||||
compiler_backend_path = null;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_FRIDA_GLIB
|
||||
private Mutex gc_mutex;
|
||||
private uint gc_generation = 0;
|
||||
private bool gc_scheduled = false;
|
||||
#endif
|
||||
|
||||
public void on_pending_garbage (void * data) {
|
||||
#if HAVE_FRIDA_GLIB
|
||||
gc_mutex.lock ();
|
||||
gc_generation++;
|
||||
bool already_scheduled = gc_scheduled;
|
||||
@@ -3613,5 +3679,6 @@ namespace Frida {
|
||||
|
||||
return repeat;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
get { return device.icon; }
|
||||
get { return device.icon ?? _network_icon; }
|
||||
}
|
||||
|
||||
public HostSessionProviderKind kind {
|
||||
@@ -70,6 +70,11 @@ namespace Frida {
|
||||
|
||||
private FruityHostSession? host_session;
|
||||
private string _name;
|
||||
private static Variant _network_icon;
|
||||
|
||||
static construct {
|
||||
_network_icon = make_provider_icon (Frida.Data.Icons.get_fruity_network_png_blob ().data);
|
||||
}
|
||||
|
||||
public FruityHostSessionProvider (Fruity.Device device) {
|
||||
Object (device: device);
|
||||
@@ -85,7 +90,21 @@ namespace Frida {
|
||||
if (host_session != null)
|
||||
throw new Error.INVALID_OPERATION ("Already created");
|
||||
|
||||
host_session = new FruityHostSession (device);
|
||||
uint16 control_port = DEFAULT_CONTROL_PORT;
|
||||
|
||||
if (options != null) {
|
||||
Value? control_endpoint_val = options.map["control-endpoint"];
|
||||
if (control_endpoint_val != null) {
|
||||
if (!control_endpoint_val.holds (typeof (string)))
|
||||
throw new Error.INVALID_ARGUMENT ("The control-endpoint option must be a string");
|
||||
unowned string control_endpoint = control_endpoint_val.get_string ();
|
||||
if (!control_endpoint.has_prefix ("tcp:"))
|
||||
throw new Error.INVALID_ARGUMENT ("The control-endpoint must be TCP-flavored");
|
||||
control_port = (uint16) uint.parse (control_endpoint[4:]);
|
||||
}
|
||||
}
|
||||
|
||||
host_session = new FruityHostSession (device, control_port);
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
|
||||
return host_session;
|
||||
@@ -172,6 +191,11 @@ namespace Frida {
|
||||
construct;
|
||||
}
|
||||
|
||||
public uint16 control_port {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private Gee.Map<uint, Fruity.ProcessControlService> pids_launched_without_lldb =
|
||||
new Gee.HashMap<uint, Fruity.ProcessControlService> ();
|
||||
private Gee.HashMap<uint, LLDBSession> lldb_sessions = new Gee.HashMap<uint, LLDBSession> ();
|
||||
@@ -205,8 +229,8 @@ namespace Frida {
|
||||
DEBUGSERVER_ENDPOINT_LEGACY,
|
||||
};
|
||||
|
||||
public FruityHostSession (Fruity.Device device) {
|
||||
Object (device: device);
|
||||
public FruityHostSession (Fruity.Device device, uint16 control_port) {
|
||||
Object (device: device, control_port: control_port);
|
||||
}
|
||||
|
||||
construct {
|
||||
@@ -959,6 +983,7 @@ namespace Frida {
|
||||
if (lldb_session != null) {
|
||||
var gadget_details = yield lldb_session.query_gadget_details (cancellable);
|
||||
|
||||
lldb_session.attach_count++;
|
||||
return yield attach_via_gadget (pid, options, gadget_details, cancellable);
|
||||
}
|
||||
|
||||
@@ -985,6 +1010,7 @@ namespace Frida {
|
||||
|
||||
var gadget_details = yield lldb_session.query_gadget_details (cancellable);
|
||||
|
||||
lldb_session.attach_count++;
|
||||
return yield attach_via_gadget (pid, options, gadget_details, cancellable);
|
||||
}
|
||||
|
||||
@@ -1232,11 +1258,14 @@ namespace Frida {
|
||||
|
||||
var lldb_session = lldb_sessions[entry.pid];
|
||||
if (lldb_session != null) {
|
||||
remove_lldb_session (lldb_session);
|
||||
if (reason == SessionDetachReason.APPLICATION_REQUESTED)
|
||||
yield entry.detach_lldb (lldb_session, io_cancellable);
|
||||
else
|
||||
yield lldb_session.close (io_cancellable);
|
||||
lldb_session.attach_count--;
|
||||
if (lldb_session.attach_count == 0) {
|
||||
remove_lldb_session (lldb_session);
|
||||
if (reason == SessionDetachReason.APPLICATION_REQUESTED)
|
||||
yield entry.detach_lldb (lldb_session, io_cancellable);
|
||||
else
|
||||
yield lldb_session.close (io_cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
gadget_entries.unset (id);
|
||||
@@ -1383,7 +1412,7 @@ namespace Frida {
|
||||
: Fruity.OpenTcpChannelFlags.ALLOW_TUNNEL;
|
||||
|
||||
try {
|
||||
return yield device.open_tcp_channel (DEFAULT_CONTROL_PORT.to_string (), open_flags, cancellable);
|
||||
return yield device.open_tcp_channel (control_port.to_string (), open_flags, cancellable);
|
||||
} catch (Error e) {
|
||||
pending_error = e;
|
||||
if (!(e is Error.SERVER_NOT_RUNNING))
|
||||
@@ -1500,6 +1529,8 @@ namespace Frida {
|
||||
construct;
|
||||
}
|
||||
|
||||
public uint attach_count = 0;
|
||||
|
||||
private Promise<Fruity.Injector.GadgetDetails>? gadget_request;
|
||||
private ulong exception_handler = 0;
|
||||
private string? main_thread_id;
|
||||
@@ -2859,7 +2890,15 @@ namespace Frida {
|
||||
foreach (var pid in pids_to_trace) {
|
||||
if (target_pids.has_key (pid))
|
||||
continue;
|
||||
var sig = yield info_service.query_symbolicator_signature (pid, cancellable);
|
||||
|
||||
Fruity.CsSignature sig;
|
||||
try {
|
||||
sig = yield info_service.query_symbolicator_signature (pid, cancellable);
|
||||
} catch (Error e) {
|
||||
sig = new Fruity.CsSignature ();
|
||||
sig.pid = pid;
|
||||
}
|
||||
|
||||
target_pids[pid] = sig;
|
||||
}
|
||||
l.free ();
|
||||
@@ -2912,6 +2951,18 @@ namespace Frida {
|
||||
return reply.end ();
|
||||
}
|
||||
|
||||
if (type == "include-syscalls") {
|
||||
reader.read_member ("native");
|
||||
var nrs = read_int32_array (reader);
|
||||
reader.end_member ();
|
||||
|
||||
var l = new MutexLocker (mutex);
|
||||
excluded_syscalls.remove_all_array (nrs);
|
||||
l.free ();
|
||||
|
||||
return reply.end ();
|
||||
}
|
||||
|
||||
throw new Error.INVALID_ARGUMENT ("Unsupported request type: %s", type);
|
||||
}
|
||||
|
||||
|
||||
@@ -186,8 +186,14 @@ namespace Frida.Fruity.Injector {
|
||||
}
|
||||
|
||||
private async void teardown (Cancellable? cancellable) throws GLib.Error {
|
||||
yield lldb.deallocate (scratch_page, cancellable);
|
||||
yield lldb.deallocate (jit_page, cancellable);
|
||||
try {
|
||||
yield lldb.deallocate (scratch_page, cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
try {
|
||||
yield lldb.deallocate (jit_page, cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
|
||||
yield restore_main_thread_state (cancellable);
|
||||
}
|
||||
@@ -893,11 +899,14 @@ namespace Frida.Fruity.Injector {
|
||||
uint64 initializer = (notify_objc_init != null) ? notify_objc_init : libdyld_initialize;
|
||||
GDB.Breakpoint init_breakpoint = yield lldb.add_breakpoint (SOFT, initializer, 4, cancellable);
|
||||
GDB.Breakpoint? restart_breakpoint = null;
|
||||
uint64? restart_with_dyld_in_cache = dyld_symbols["__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN6mach_o6HeaderEPK15DyldSharedCachePv"];
|
||||
if (restart_with_dyld_in_cache == null)
|
||||
restart_with_dyld_in_cache = dyld_symbols["__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPK15DyldSharedCachePv"];
|
||||
if (restart_with_dyld_in_cache == null)
|
||||
restart_with_dyld_in_cache = dyld_symbols["__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPv"];
|
||||
|
||||
uint64? restart_with_dyld_in_cache = null;
|
||||
foreach (var e in dyld_symbols.entries) {
|
||||
if (e.key.contains ("restartWithDyldInCache")) {
|
||||
restart_with_dyld_in_cache = e.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (restart_with_dyld_in_cache != null)
|
||||
restart_breakpoint = yield lldb.add_breakpoint (SOFT, restart_with_dyld_in_cache, 4, cancellable);
|
||||
|
||||
@@ -1374,8 +1383,12 @@ namespace Frida.Fruity.Injector {
|
||||
}
|
||||
}
|
||||
|
||||
if (state != scratch_page)
|
||||
yield lldb.deallocate (state, cancellable);
|
||||
if (state != scratch_page) {
|
||||
try {
|
||||
yield lldb.deallocate (state, cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
return new SymbolSet (symbols);
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ namespace Frida.LLDB {
|
||||
|
||||
Json.Reader reader;
|
||||
try {
|
||||
reader = new Json.Reader (Json.from_string (response.payload));
|
||||
reader = make_json_reader (response.payload);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.PROTOCOL ("Invalid response");
|
||||
}
|
||||
@@ -194,7 +194,7 @@ namespace Frida.LLDB {
|
||||
|
||||
Json.Reader reader;
|
||||
try {
|
||||
reader = new Json.Reader (Json.from_string (response.payload));
|
||||
reader = make_json_reader (response.payload);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.PROTOCOL ("Invalid response");
|
||||
}
|
||||
|
||||
@@ -1592,8 +1592,11 @@ namespace Frida.Fruity {
|
||||
if (t == typeof (string))
|
||||
return str_hash (v.get_string ());
|
||||
|
||||
if (t == typeof (Bytes) || t == typeof (PlistDict) || t == typeof (PlistArray))
|
||||
return (uint) v.get_object ();
|
||||
if (t == typeof (Bytes))
|
||||
return ((Bytes) v.get_boxed ()).hash ();
|
||||
|
||||
if (t == typeof (PlistDict) || t == typeof (PlistArray))
|
||||
return (uint) (size_t) v.get_object ();
|
||||
|
||||
if (t == typeof (PlistUid))
|
||||
return (uint) ((PlistUid) v.get_object ()).uid;
|
||||
@@ -1623,7 +1626,10 @@ namespace Frida.Fruity {
|
||||
if (t == typeof (string))
|
||||
return a.get_string () == b.get_string ();
|
||||
|
||||
if (t == typeof (Bytes) || t == typeof (PlistDict) || t == typeof (PlistArray))
|
||||
if (t == typeof (Bytes))
|
||||
return ((Bytes) a.get_boxed ()).compare ((Bytes) b.get_boxed ()) == 0;
|
||||
|
||||
if (t == typeof (PlistDict) || t == typeof (PlistArray))
|
||||
return a.get_object () == b.get_object ();
|
||||
|
||||
if (t == typeof (PlistNull))
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public sealed class TlsPskClientStream : IOStream, AsyncInitable {
|
||||
public IOStream base_stream {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public string psk_identity {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public Bytes psk {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public override InputStream input_stream {
|
||||
get {
|
||||
return _input_stream;
|
||||
}
|
||||
}
|
||||
|
||||
public override OutputStream output_stream {
|
||||
get {
|
||||
return _output_stream;
|
||||
}
|
||||
}
|
||||
|
||||
private OpenSSL.SSLContext ssl_ctx;
|
||||
private OpenSSL.SSL ssl;
|
||||
private void * rbio;
|
||||
private void * wbio;
|
||||
|
||||
private PollableInputStream base_pollable_input;
|
||||
private PollableOutputStream base_pollable_output;
|
||||
|
||||
private TlsPskInputStream _input_stream;
|
||||
private TlsPskOutputStream _output_stream;
|
||||
|
||||
private const int IO_CHUNK_SIZE = 64 * 1024;
|
||||
private const string CIPHER_LIST =
|
||||
"PSK-AES128-GCM-SHA256:PSK-AES256-GCM-SHA384:PSK-AES256-CBC-SHA384:PSK-AES128-CBC-SHA256";
|
||||
|
||||
public static async TlsPskClientStream open (IOStream base_stream, string psk_identity, Bytes psk,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
var stream = new TlsPskClientStream (base_stream, psk_identity, psk);
|
||||
|
||||
try {
|
||||
yield stream.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
private TlsPskClientStream (IOStream base_stream, string psk_identity, Bytes psk) {
|
||||
Object (base_stream: base_stream, psk_identity: psk_identity, psk: psk);
|
||||
}
|
||||
|
||||
construct {
|
||||
var raw_input = base_stream.get_input_stream ();
|
||||
var raw_output = base_stream.get_output_stream ();
|
||||
assert (raw_input is PollableInputStream && raw_output is PollableOutputStream);
|
||||
base_pollable_input = (PollableInputStream) raw_input;
|
||||
base_pollable_output = (PollableOutputStream) raw_output;
|
||||
|
||||
_input_stream = new TlsPskInputStream (this);
|
||||
_output_stream = new TlsPskOutputStream (this);
|
||||
|
||||
ssl_ctx = new OpenSSL.SSLContext (OpenSSL.SSLMethod.tls_client ());
|
||||
|
||||
ssl = new OpenSSL.SSL (ssl_ctx);
|
||||
ssl.set_app_data (this);
|
||||
ssl.set_connect_state ();
|
||||
ssl.set_cipher_list (CIPHER_LIST);
|
||||
ssl.set_psk_client_callback ((ssl, hint, identity, psk) => {
|
||||
TlsPskClientStream * self = ssl.get_app_data ();
|
||||
|
||||
unowned uint8[] ident_bytes = self->psk_identity.data;
|
||||
Memory.copy (identity, ident_bytes, ident_bytes.length);
|
||||
|
||||
unowned uint8[] key = self->psk.get_data ();
|
||||
Memory.copy (psk, key, key.length);
|
||||
|
||||
return key.length;
|
||||
});
|
||||
|
||||
rbio = OpenSSL.BasicIO.raw_new (OpenSSL.BasicIOMethod.memory ());
|
||||
wbio = OpenSSL.BasicIO.raw_new (OpenSSL.BasicIOMethod.memory ());
|
||||
ssl.set_bio (rbio, wbio);
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
while (true) {
|
||||
int ret = ssl.do_handshake ();
|
||||
|
||||
yield handshake_drain_wbio (cancellable);
|
||||
|
||||
if (ret == 1)
|
||||
return true;
|
||||
|
||||
int err = ssl.get_error (ret);
|
||||
if (err == OpenSSL.SSLErrorCode.WANT_READ) {
|
||||
if (!yield handshake_feed_rbio (cancellable))
|
||||
throw new Error.TRANSPORT ("Connection closed during TLS handshake");
|
||||
} else if (err != OpenSSL.SSLErrorCode.WANT_WRITE) {
|
||||
throw new Error.PROTOCOL ("TLS handshake failed: %s", openssl_error_string ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void handshake_drain_wbio (Cancellable? cancellable) throws Error, IOError {
|
||||
while (OpenSSL.BasicIO.raw_ctrl_pending (wbio) > 0) {
|
||||
var buf = new uint8[IO_CHUNK_SIZE];
|
||||
int n = OpenSSL.BasicIO.raw_read (wbio, buf);
|
||||
try {
|
||||
yield ((OutputStream) base_pollable_output).write_all_async (buf[0:n], Priority.DEFAULT,
|
||||
cancellable, null);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async bool handshake_feed_rbio (Cancellable? cancellable) throws Error, IOError {
|
||||
var buf = new uint8[IO_CHUNK_SIZE];
|
||||
ssize_t n;
|
||||
try {
|
||||
n = yield ((InputStream) base_pollable_input).read_async (buf, Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
if (n == 0)
|
||||
return false;
|
||||
OpenSSL.BasicIO.raw_write (rbio, buf[:n]);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal ssize_t do_read_nonblocking (uint8[] buffer) throws GLib.Error {
|
||||
while (true) {
|
||||
int n = ssl.read (buffer);
|
||||
if (n > 0)
|
||||
return n;
|
||||
|
||||
int err = ssl.get_error (n);
|
||||
if (err == OpenSSL.SSLErrorCode.ZERO_RETURN)
|
||||
return 0;
|
||||
if (err != OpenSSL.SSLErrorCode.WANT_READ)
|
||||
throw new IOError.FAILED ("TLS read error: %s", openssl_error_string ());
|
||||
|
||||
var tmp = new uint8[IO_CHUNK_SIZE];
|
||||
ssize_t r = base_pollable_input.read_nonblocking (tmp);
|
||||
if (r == 0)
|
||||
return 0;
|
||||
OpenSSL.BasicIO.raw_write (rbio, tmp[:r]);
|
||||
}
|
||||
}
|
||||
|
||||
internal ssize_t do_read_blocking (uint8[] buffer, Cancellable? cancellable) throws IOError {
|
||||
while (true) {
|
||||
int n = ssl.read (buffer);
|
||||
if (n > 0)
|
||||
return n;
|
||||
|
||||
int err = ssl.get_error (n);
|
||||
if (err == OpenSSL.SSLErrorCode.ZERO_RETURN)
|
||||
return 0;
|
||||
if (err != OpenSSL.SSLErrorCode.WANT_READ)
|
||||
throw new IOError.FAILED ("TLS read error: %s", openssl_error_string ());
|
||||
|
||||
var tmp = new uint8[IO_CHUNK_SIZE];
|
||||
ssize_t r = ((InputStream) base_pollable_input).read (tmp, cancellable);
|
||||
if (r == 0)
|
||||
return 0;
|
||||
OpenSSL.BasicIO.raw_write (rbio, tmp[:r]);
|
||||
}
|
||||
}
|
||||
|
||||
internal bool do_is_readable () {
|
||||
return ssl.pending () > 0 || base_pollable_input.is_readable ();
|
||||
}
|
||||
|
||||
internal PollableSource do_create_input_source (Cancellable? cancellable) {
|
||||
return new PollableSource.full (_input_stream,
|
||||
base_pollable_input.create_source (cancellable), cancellable);
|
||||
}
|
||||
|
||||
internal ssize_t do_write_nonblocking (uint8[] buffer) throws GLib.Error {
|
||||
if (!flush_wbio_nonblocking ())
|
||||
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
|
||||
|
||||
int n = ssl.write (buffer);
|
||||
if (n <= 0) {
|
||||
int err = ssl.get_error (n);
|
||||
if (err == OpenSSL.SSLErrorCode.WANT_WRITE || err == OpenSSL.SSLErrorCode.WANT_READ)
|
||||
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
|
||||
throw new IOError.FAILED ("TLS write error: %s", openssl_error_string ());
|
||||
}
|
||||
|
||||
flush_wbio_nonblocking ();
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
internal ssize_t do_write_blocking (uint8[] buffer, Cancellable? cancellable) throws IOError {
|
||||
try {
|
||||
flush_wbio_blocking (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new IOError.FAILED ("%s", e.message);
|
||||
}
|
||||
|
||||
int n = ssl.write (buffer);
|
||||
if (n <= 0)
|
||||
throw new IOError.FAILED ("TLS write error: %s", openssl_error_string ());
|
||||
|
||||
try {
|
||||
flush_wbio_blocking (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new IOError.FAILED ("%s", e.message);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
internal bool do_is_writable () {
|
||||
return base_pollable_output.is_writable ();
|
||||
}
|
||||
|
||||
internal PollableSource do_create_output_source (Cancellable? cancellable) {
|
||||
return new PollableSource.full (_output_stream, base_pollable_output.create_source (cancellable), cancellable);
|
||||
}
|
||||
|
||||
private bool flush_wbio_nonblocking () throws GLib.Error {
|
||||
while (true) {
|
||||
unowned uint8[] data;
|
||||
long len = OpenSSL.BasicIO.raw_get_mem_data (wbio, out data);
|
||||
if (len == 0)
|
||||
return true;
|
||||
data.length = (int) len;
|
||||
|
||||
ssize_t written;
|
||||
try {
|
||||
written = base_pollable_output.write_nonblocking (data);
|
||||
} catch (IOError.WOULD_BLOCK e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var scratch = new uint8[written];
|
||||
OpenSSL.BasicIO.raw_read (wbio, scratch);
|
||||
}
|
||||
}
|
||||
|
||||
private void flush_wbio_blocking (Cancellable? cancellable) throws GLib.Error {
|
||||
while (true) {
|
||||
unowned uint8[] data;
|
||||
long len = OpenSSL.BasicIO.raw_get_mem_data (wbio, out data);
|
||||
if (len == 0)
|
||||
return;
|
||||
data.length = (int) len;
|
||||
|
||||
size_t written;
|
||||
((OutputStream) base_pollable_output).write_all (data, out written, cancellable);
|
||||
|
||||
var scratch = new uint8[written];
|
||||
OpenSSL.BasicIO.raw_read (wbio, scratch);
|
||||
}
|
||||
}
|
||||
|
||||
private static string openssl_error_string () {
|
||||
var buf = new char[256];
|
||||
OpenSSL.Error.error_string_n (OpenSSL.Error.get_error (), buf);
|
||||
return (string) buf;
|
||||
}
|
||||
|
||||
private class TlsPskInputStream : InputStream, PollableInputStream {
|
||||
public weak TlsPskClientStream parent {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public TlsPskInputStream (TlsPskClientStream parent) {
|
||||
Object (parent: parent);
|
||||
}
|
||||
|
||||
public override ssize_t read (uint8[] buffer, Cancellable? cancellable = null) throws IOError {
|
||||
return parent.do_read_blocking (buffer, cancellable);
|
||||
}
|
||||
|
||||
public override bool close (Cancellable? cancellable = null) throws IOError {
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool can_poll () {
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool is_readable () {
|
||||
return parent.do_is_readable ();
|
||||
}
|
||||
|
||||
public PollableSource create_source (Cancellable? cancellable) {
|
||||
return parent.do_create_input_source (cancellable);
|
||||
}
|
||||
|
||||
public ssize_t read_nonblocking_fn (uint8[] buffer) throws GLib.Error {
|
||||
return parent.do_read_nonblocking (buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private class TlsPskOutputStream : OutputStream, PollableOutputStream {
|
||||
public weak TlsPskClientStream parent {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public TlsPskOutputStream (TlsPskClientStream parent) {
|
||||
Object (parent: parent);
|
||||
}
|
||||
|
||||
public override ssize_t write (uint8[] buffer, Cancellable? cancellable = null) throws IOError {
|
||||
return parent.do_write_blocking (buffer, cancellable);
|
||||
}
|
||||
|
||||
public override bool close (Cancellable? cancellable = null) throws IOError {
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool flush (Cancellable? cancellable = null) throws GLib.Error {
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool can_poll () {
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool is_writable () {
|
||||
return parent.do_is_writable ();
|
||||
}
|
||||
|
||||
public PollableSource create_source (Cancellable? cancellable) {
|
||||
return parent.do_create_output_source (cancellable);
|
||||
}
|
||||
|
||||
public ssize_t write_nonblocking_fn (uint8[]? buffer) throws GLib.Error {
|
||||
return parent.do_write_nonblocking (buffer);
|
||||
}
|
||||
|
||||
public PollableReturn writev_nonblocking_fn (OutputVector[] vectors, out size_t bytes_written) throws GLib.Error {
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,6 +183,19 @@ namespace Frida {
|
||||
public abstract void unlink_service_session (HostSession host_session, ServiceSessionId id);
|
||||
}
|
||||
|
||||
private const uint16 PROVIDER_ICON_SIZE = 96;
|
||||
|
||||
internal Variant make_provider_icon (uint8[] png) {
|
||||
var image = new Bytes.static (png);
|
||||
var builder = new VariantBuilder (VariantType.VARDICT);
|
||||
builder.add ("{sv}", "format", new Variant.string ("png"));
|
||||
builder.add ("{sv}", "width", new Variant.uint16 (PROVIDER_ICON_SIZE));
|
||||
builder.add ("{sv}", "height", new Variant.uint16 (PROVIDER_ICON_SIZE));
|
||||
builder.add ("{sv}", "image", Variant.new_from_data (new VariantType ("ay"),
|
||||
image.get_data (), true, image));
|
||||
return builder.end ();
|
||||
}
|
||||
|
||||
public interface HostSessionConnection : Object {
|
||||
public abstract HostSession host_session {
|
||||
get;
|
||||
@@ -231,6 +244,12 @@ namespace Frida {
|
||||
set;
|
||||
default = new Gee.HashMap<string, Value?> ();
|
||||
}
|
||||
|
||||
public HostSessionOptions copy () {
|
||||
var opts = new HostSessionOptions ();
|
||||
opts.map.set_all (map);
|
||||
return opts;
|
||||
}
|
||||
}
|
||||
|
||||
public interface HostChannelProvider : Object {
|
||||
@@ -303,7 +322,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
protected virtual Variant? load_icon () {
|
||||
return null;
|
||||
return make_provider_icon (Frida.Data.Icons.get_local_png_blob ().data);
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[resource-compiler]
|
||||
namespace = Frida.Data.Icons
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
@@ -12,6 +12,7 @@ namespace Frida {
|
||||
private Gee.Map<uint, ExecTransitionSession> exec_transitions = new Gee.HashMap<uint, ExecTransitionSession> ();
|
||||
private Gee.Map<uint, AwaitExecTransitionTask> exec_waiters = new Gee.HashMap<uint, AwaitExecTransitionTask> ();
|
||||
private Gee.Map<uint, PausedSyscallSession> paused_syscalls = new Gee.HashMap<uint, PausedSyscallSession> ();
|
||||
private Gee.Map<uint, InjectSession> suspended_by_inject = new Gee.HashMap<uint, InjectSession> ();
|
||||
private Gee.Map<uint, RemoteAgent> agents = new Gee.HashMap<uint, RemoteAgent> ();
|
||||
private Gee.Map<uint, Source> agent_expiries = new Gee.HashMap<uint, Source> ();
|
||||
private Gee.Map<uint, Gee.Queue<TaskEntry>> task_queues = new Gee.HashMap<uint, Gee.Queue<TaskEntry>> ();
|
||||
@@ -276,6 +277,12 @@ namespace Frida {
|
||||
return null;
|
||||
}
|
||||
|
||||
InjectSession inject_session;
|
||||
if (backend.suspended_by_inject.unset (pid, out inject_session)) {
|
||||
inject_session.close ();
|
||||
return null;
|
||||
}
|
||||
|
||||
SpawnedProcess? p = backend.spawned_processes[pid];
|
||||
if (p != null) {
|
||||
p.resume ();
|
||||
@@ -313,7 +320,10 @@ namespace Frida {
|
||||
yield pss.interrupt (cancellable);
|
||||
var session = yield InjectSession.open (pid, cancellable);
|
||||
RemoteAgent agent = yield session.inject (spec, cancellable);
|
||||
session.close ();
|
||||
if (session.was_group_stopped)
|
||||
backend.suspended_by_inject[pid] = session;
|
||||
else
|
||||
session.close ();
|
||||
return agent;
|
||||
}
|
||||
}
|
||||
@@ -1819,6 +1829,11 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public bool was_group_stopped {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public enum InitBehavior {
|
||||
INTERRUPT,
|
||||
CONTINUE
|
||||
@@ -1865,6 +1880,8 @@ namespace Frida {
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
PtraceOptions options = PtraceOptions.TRACESYSGOOD | PtraceOptions.TRACEEXEC;
|
||||
|
||||
bool was_stopped = seize_supported && query_process_state (tid) == 'T';
|
||||
|
||||
PtraceRequest req;
|
||||
long res;
|
||||
if (seize_supported) {
|
||||
@@ -1890,8 +1907,14 @@ namespace Frida {
|
||||
attach_state = ATTACHED;
|
||||
|
||||
if (seize_supported) {
|
||||
ptrace (INTERRUPT, tid);
|
||||
yield wait_for_signal (TRAP, cancellable);
|
||||
if (was_stopped) {
|
||||
was_group_stopped = true;
|
||||
yield wait_for_next_stop (cancellable);
|
||||
Posix.kill ((Posix.pid_t) pid, Posix.Signal.CONT);
|
||||
} else {
|
||||
ptrace (INTERRUPT, tid);
|
||||
yield wait_for_signal (TRAP, cancellable);
|
||||
}
|
||||
} else {
|
||||
yield wait_for_signal (STOP, cancellable);
|
||||
ptrace (SETOPTIONS, tid, null, (void *) options);
|
||||
@@ -1920,6 +1943,18 @@ namespace Frida {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static char query_process_state (uint tid) {
|
||||
try {
|
||||
string stat;
|
||||
FileUtils.get_contents ("/proc/%u/stat".printf (tid), out stat);
|
||||
int paren_end = stat.last_index_of_char (')');
|
||||
if (paren_end != -1 && paren_end + 2 < stat.length)
|
||||
return stat[paren_end + 2];
|
||||
} catch (FileError e) {
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
|
||||
public void close () throws Error {
|
||||
if (attach_state == ATTACHED) {
|
||||
ptrace (DETACH, tid);
|
||||
|
||||
@@ -36,6 +36,18 @@ namespace Frida {
|
||||
_resource_store = null;
|
||||
}
|
||||
|
||||
public async void preload (Cancellable? cancellable) throws IOError {
|
||||
try {
|
||||
yield obtain_for_64bit (cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
|
||||
try {
|
||||
yield obtain_for_32bit (cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private ResourceStore get_resource_store () throws Error {
|
||||
if (_resource_store == null)
|
||||
_resource_store = new ResourceStore (tempdir);
|
||||
@@ -189,6 +201,10 @@ namespace Frida {
|
||||
|
||||
private async LinuxHelper obtain_for_32bit (Cancellable? cancellable) throws Error, IOError {
|
||||
if (factory32 == null) {
|
||||
#if ANDROID
|
||||
if (sizeof (void *) == 8 && _query_android_system_property ("ro.product.cpu.abilist32") == "")
|
||||
throw new Error.NOT_SUPPORTED ("Android system does not support 32-bit processes");
|
||||
#endif
|
||||
var store = get_resource_store ();
|
||||
if (sizeof (void *) != 4 && store.helper32 == null)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to handle 32-bit processes due to build configuration");
|
||||
@@ -634,7 +650,7 @@ namespace Frida {
|
||||
if (blob64.data.length > 0)
|
||||
helper64 = make_temporary_helper ("frida-helper-64", blob64.data);
|
||||
#else
|
||||
var tpl = PathTemplate (Config.FRIDA_HELPER_PATH);
|
||||
var tpl = PathTemplate (Frida.helper_path);
|
||||
string path = tpl.expand ((sizeof (void *) == 8) ? "32" : "64");
|
||||
HelperFile file = new InstalledHelperFile.for_path (path);
|
||||
if (sizeof (void *) == 8)
|
||||
|
||||
@@ -36,6 +36,7 @@ bpf_noarch_progs := \
|
||||
activity-sampler \
|
||||
$(NULL)
|
||||
bpf_arch_progs := \
|
||||
spawn-gater \
|
||||
syscall-tracer \
|
||||
$(NULL)
|
||||
bpf_noarch_pairs := \
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "frida-linux-syscalls.h"
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
#define SIGSTOP 19
|
||||
#define MAX_FILENAME 256
|
||||
|
||||
typedef struct _ExecveEvent ExecveEvent;
|
||||
|
||||
struct _ExecveEvent
|
||||
{
|
||||
int pid;
|
||||
char command[MAX_FILENAME];
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
__uint (type, BPF_MAP_TYPE_RINGBUF);
|
||||
__uint (max_entries, 1 << 22);
|
||||
}
|
||||
events SEC (".maps");
|
||||
|
||||
struct trace_event_raw_sys_enter
|
||||
{
|
||||
__u64 unused;
|
||||
long id;
|
||||
unsigned long args[6];
|
||||
};
|
||||
|
||||
SEC ("tracepoint/raw_syscalls/sys_enter")
|
||||
int
|
||||
on_execve_enter (struct trace_event_raw_sys_enter * ctx)
|
||||
{
|
||||
__s32 nr = (__s32) ctx->id;
|
||||
if (nr != FRIDA_LINUX_SYSCALL_EXECVE)
|
||||
return 0;
|
||||
|
||||
ExecveEvent * e = bpf_ringbuf_reserve (&events, sizeof (ExecveEvent), 0);
|
||||
if (e == NULL)
|
||||
return 0;
|
||||
|
||||
e->pid = bpf_get_current_pid_tgid () >> 32;
|
||||
const char * filename = (const char *) ctx->args[0];
|
||||
bpf_probe_read_user_str (e->command, sizeof (e->command), filename);
|
||||
|
||||
bpf_ringbuf_submit (e, 0);
|
||||
|
||||
bpf_send_signal (SIGSTOP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char LICENSE[] SEC ("license") = "Dual BSD/GPL";
|
||||
@@ -38,6 +38,8 @@ namespace Frida {
|
||||
private CrashMonitor? crash_monitor;
|
||||
#endif
|
||||
|
||||
private SpawnGater? spawn_gater;
|
||||
|
||||
#if !ANDROID
|
||||
private ApplicationEnumerator application_enumerator = new ApplicationEnumerator ();
|
||||
#endif
|
||||
@@ -75,8 +77,8 @@ namespace Frida {
|
||||
|
||||
#if ANDROID
|
||||
robo_launcher = new RoboLauncher (this, io_cancellable);
|
||||
robo_launcher.spawn_added.connect (on_robo_launcher_spawn_added);
|
||||
robo_launcher.spawn_removed.connect (on_robo_launcher_spawn_removed);
|
||||
robo_launcher.spawn_added.connect (on_spawn_added);
|
||||
robo_launcher.spawn_removed.connect (on_spawn_removed);
|
||||
|
||||
if (report_crashes) {
|
||||
crash_monitor = new CrashMonitor ();
|
||||
@@ -95,8 +97,8 @@ namespace Frida {
|
||||
public override async void close (Cancellable? cancellable) throws IOError {
|
||||
#if ANDROID
|
||||
yield robo_launcher.close (cancellable);
|
||||
robo_launcher.spawn_added.disconnect (on_robo_launcher_spawn_added);
|
||||
robo_launcher.spawn_removed.disconnect (on_robo_launcher_spawn_removed);
|
||||
robo_launcher.spawn_added.disconnect (on_spawn_added);
|
||||
robo_launcher.spawn_removed.disconnect (on_spawn_removed);
|
||||
|
||||
if (android_helper_request != null) {
|
||||
try {
|
||||
@@ -107,6 +109,13 @@ namespace Frida {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (spawn_gater != null) {
|
||||
spawn_gater.spawn_added.disconnect (on_spawn_added);
|
||||
spawn_gater.spawn_removed.disconnect (on_spawn_removed);
|
||||
spawn_gater.stop ();
|
||||
spawn_gater = null;
|
||||
}
|
||||
|
||||
yield base.close (cancellable);
|
||||
|
||||
#if ANDROID
|
||||
@@ -153,7 +162,7 @@ namespace Frida {
|
||||
tpl = agent.get_path_template ();
|
||||
}
|
||||
#else
|
||||
tpl = PathTemplate (Config.FRIDA_AGENT_PATH);
|
||||
tpl = PathTemplate (Frida.agent_path);
|
||||
#endif
|
||||
if (path == null)
|
||||
path = tpl.expand (arch_name);
|
||||
@@ -268,27 +277,58 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public override async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
var helper_process = helper as LinuxHelperProcess;
|
||||
if (helper_process != null)
|
||||
yield helper_process.preload (cancellable);
|
||||
|
||||
var gater = ensure_spawn_gater ();
|
||||
if (gater.state == STOPPED) {
|
||||
#if ANDROID
|
||||
try {
|
||||
gater.start ();
|
||||
} catch (Error e) {
|
||||
}
|
||||
#else
|
||||
gater.start ();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
yield robo_launcher.enable_spawn_gating (cancellable);
|
||||
#else
|
||||
throw new Error.NOT_SUPPORTED ("Not yet supported on this OS");
|
||||
#endif
|
||||
}
|
||||
|
||||
public override async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
spawn_gater?.stop ();
|
||||
|
||||
#if ANDROID
|
||||
yield robo_launcher.disable_spawn_gating (cancellable);
|
||||
#else
|
||||
throw new Error.NOT_SUPPORTED ("Not yet supported on this OS");
|
||||
#endif
|
||||
}
|
||||
|
||||
private SpawnGater ensure_spawn_gater () {
|
||||
if (spawn_gater == null) {
|
||||
spawn_gater = new SpawnGater (helper);
|
||||
spawn_gater.spawn_added.connect (on_spawn_added);
|
||||
spawn_gater.spawn_removed.connect (on_spawn_removed);
|
||||
}
|
||||
return spawn_gater;
|
||||
}
|
||||
|
||||
public override async HostSpawnInfo[] enumerate_pending_spawn (Cancellable? cancellable) throws Error, IOError {
|
||||
var result = new HostSpawnInfo[0];
|
||||
|
||||
if (spawn_gater != null) {
|
||||
foreach (var spawn in spawn_gater.enumerate_pending_spawn ())
|
||||
result += spawn;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
return robo_launcher.enumerate_pending_spawn ();
|
||||
#else
|
||||
throw new Error.NOT_SUPPORTED ("Not yet supported on this OS");
|
||||
foreach (var spawn in robo_launcher.enumerate_pending_spawn ())
|
||||
result += spawn;
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override async uint spawn (string program, HostSpawnOptions options, Cancellable? cancellable)
|
||||
@@ -322,6 +362,9 @@ namespace Frida {
|
||||
}
|
||||
|
||||
protected override async void perform_resume (uint pid, Cancellable? cancellable) throws Error, IOError {
|
||||
if (spawn_gater != null && spawn_gater.try_resume (pid))
|
||||
return;
|
||||
|
||||
#if ANDROID
|
||||
if (robo_launcher.try_resume (pid))
|
||||
return;
|
||||
@@ -351,7 +394,7 @@ namespace Frida {
|
||||
#if HAVE_EMBEDDED_ASSETS
|
||||
id = yield linjector.inject_library_resource (pid, agent, entrypoint, parameters, features, cancellable);
|
||||
#else
|
||||
id = yield linjector.inject_library_file_with_template (pid, PathTemplate (Config.FRIDA_AGENT_PATH), entrypoint,
|
||||
id = yield linjector.inject_library_file_with_template (pid, PathTemplate (Frida.agent_path), entrypoint,
|
||||
parameters, features, cancellable);
|
||||
#endif
|
||||
injectee_by_pid[pid] = id;
|
||||
@@ -444,14 +487,6 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private void on_robo_launcher_spawn_added (HostSpawnInfo info) {
|
||||
spawn_added (info);
|
||||
}
|
||||
|
||||
private void on_robo_launcher_spawn_removed (HostSpawnInfo info) {
|
||||
spawn_removed (info);
|
||||
}
|
||||
|
||||
protected override async CrashInfo? try_collect_crash (uint pid, Cancellable? cancellable) throws IOError {
|
||||
if (crash_monitor == null)
|
||||
return null;
|
||||
@@ -471,6 +506,14 @@ namespace Frida {
|
||||
}
|
||||
#endif
|
||||
|
||||
private void on_spawn_added (HostSpawnInfo info) {
|
||||
spawn_added (info);
|
||||
}
|
||||
|
||||
private void on_spawn_removed (HostSpawnInfo info) {
|
||||
spawn_removed (info);
|
||||
}
|
||||
|
||||
private void on_output (uint pid, int fd, uint8[] data) {
|
||||
output (pid, fd, data);
|
||||
}
|
||||
@@ -613,6 +656,17 @@ namespace Frida {
|
||||
vm_mod = dlopen_ext (vm_soname, LAZY | LOCAL, info);
|
||||
} else {
|
||||
vm_mod = Android.Module.open (vm_soname, LAZY | LOCAL);
|
||||
|
||||
if (vm_mod == null) {
|
||||
var libdir = ((sizeof (void *) == 8) ? "lib64" : "lib");
|
||||
ensure_directories_on_ld_library_path ({
|
||||
"/apex/com.android.runtime/" + libdir,
|
||||
"/apex/com.android.art/" + libdir,
|
||||
"/apex/com.android.conscrypt/" + libdir
|
||||
});
|
||||
|
||||
vm_mod = Android.Module.open (vm_soname, LAZY | LOCAL);
|
||||
}
|
||||
}
|
||||
if (vm_mod == null)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to load %s: %s", vm_soname, Android.Module.get_last_error ());
|
||||
@@ -771,6 +825,31 @@ namespace Frida {
|
||||
throw e;
|
||||
}
|
||||
|
||||
private static void ensure_directories_on_ld_library_path (string[] dirs) {
|
||||
var linker = Gum.Android.get_linker_module ();
|
||||
var get_path = (GetLdLibraryPathFunc) linker.find_export_by_name ("__loader_android_get_LD_LIBRARY_PATH");
|
||||
var update_path = (UpdateLdLibraryPathFunc) linker.find_export_by_name ("__loader_android_update_LD_LIBRARY_PATH");
|
||||
|
||||
if (get_path == null || update_path == null)
|
||||
return;
|
||||
|
||||
var path_buf = new char[4096];
|
||||
get_path (path_buf);
|
||||
|
||||
var entries = new Gee.ArrayList<string> ();
|
||||
foreach (unowned string dir in dirs)
|
||||
entries.add (dir);
|
||||
|
||||
unowned string old_path = (string) path_buf;
|
||||
foreach (var dir in old_path.split (":")) {
|
||||
if (!(dir in dirs))
|
||||
entries.add (dir);
|
||||
}
|
||||
|
||||
string new_path = string.joinv (":", entries.to_array ());
|
||||
update_path (new_path);
|
||||
}
|
||||
|
||||
[CCode (has_target = false)]
|
||||
private delegate Android.Module? DlopenExtFunc (string filename, Android.DlOpenFlags flags, Android.DlExtInfo info);
|
||||
|
||||
@@ -786,6 +865,12 @@ namespace Frida {
|
||||
|
||||
[CCode (has_target = false)]
|
||||
private delegate JNI.Result RegisterFrameworkNativesLegacyFunc (void * env, JNI.ClassRef * clazz);
|
||||
|
||||
[CCode (has_target = false)]
|
||||
private delegate void GetLdLibraryPathFunc (char[] buf);
|
||||
|
||||
[CCode (has_target = false)]
|
||||
private delegate void UpdateLdLibraryPathFunc (string path);
|
||||
}
|
||||
|
||||
private class SigchainCompat : Object {
|
||||
@@ -2841,6 +2926,22 @@ namespace Frida {
|
||||
return reply.end ();
|
||||
}
|
||||
|
||||
if (type == "include-syscalls") {
|
||||
if (reader.has_member ("native")) {
|
||||
reader.read_member ("native");
|
||||
foreach_uint32 (reader, nr => tracer.include_syscall (NATIVE, nr));
|
||||
reader.end_member ();
|
||||
}
|
||||
|
||||
if (reader.has_member ("compat32")) {
|
||||
reader.read_member ("compat32");
|
||||
foreach_uint32 (reader, nr => tracer.include_syscall (COMPAT32, nr));
|
||||
reader.end_member ();
|
||||
}
|
||||
|
||||
return reply.end ();
|
||||
}
|
||||
|
||||
throw new Error.INVALID_ARGUMENT ("Unsupported request type: %s", type);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
namespace Frida {
|
||||
public sealed class SpawnGater : Object {
|
||||
public signal void spawn_added (HostSpawnInfo info);
|
||||
public signal void spawn_removed (HostSpawnInfo info);
|
||||
|
||||
public LinuxHelper helper {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public State state {
|
||||
get {
|
||||
return _state;
|
||||
}
|
||||
}
|
||||
|
||||
public enum State {
|
||||
STOPPED,
|
||||
STARTED,
|
||||
}
|
||||
|
||||
private State _state = STOPPED;
|
||||
|
||||
private Gee.Map<uint, HostSpawnInfo?> pending_spawn = new Gee.HashMap<uint, HostSpawnInfo?> ();
|
||||
|
||||
private BpfRingbufReader? events_reader;
|
||||
private IOChannel? events_channel;
|
||||
private Source? events_source;
|
||||
|
||||
private Gee.Collection<BpfLink> links = new Gee.ArrayList<BpfLink> ();
|
||||
|
||||
private const size_t RINGBUF_SIZE = 1U << 22;
|
||||
private const size_t MAX_FILENAME = 256;
|
||||
|
||||
public SpawnGater (LinuxHelper helper) {
|
||||
Object (helper: helper);
|
||||
}
|
||||
|
||||
protected override void dispose () {
|
||||
stop ();
|
||||
|
||||
base.dispose ();
|
||||
}
|
||||
|
||||
public void start () throws Error {
|
||||
assert (state == STOPPED);
|
||||
|
||||
var obj = BpfObject.open ("spawn-gater.elf", Frida.Data.HelperBackend.get_spawn_gater_elf_blob ().data);
|
||||
|
||||
var events = obj.maps.get_by_name ("events");
|
||||
|
||||
obj.prepare ();
|
||||
|
||||
events_reader = new BpfRingbufReader (events);
|
||||
|
||||
obj.load ();
|
||||
|
||||
foreach (var program in obj.programs) {
|
||||
var link = program.attach ();
|
||||
links.add (link);
|
||||
}
|
||||
|
||||
events_channel = new IOChannel.unix_new (events.fd);
|
||||
var src = new IOSource (events_channel, IOCondition.IN);
|
||||
var state = new EventsWatchState (this);
|
||||
src.set_callback (state.on_ready);
|
||||
src.attach (MainContext.get_thread_default ());
|
||||
events_source = src;
|
||||
|
||||
_state = STARTED;
|
||||
}
|
||||
|
||||
public void stop () {
|
||||
if (_state == STOPPED)
|
||||
return;
|
||||
|
||||
links.clear ();
|
||||
|
||||
events_source?.destroy ();
|
||||
events_source = null;
|
||||
|
||||
process_pending_events ();
|
||||
|
||||
foreach (var spawn in pending_spawn.values) {
|
||||
spawn_removed (spawn);
|
||||
perform_resume.begin (spawn.pid);
|
||||
}
|
||||
pending_spawn.clear ();
|
||||
|
||||
events_channel = null;
|
||||
events_reader = null;
|
||||
|
||||
_state = STOPPED;
|
||||
}
|
||||
|
||||
public HostSpawnInfo[] enumerate_pending_spawn () {
|
||||
var result = new HostSpawnInfo[pending_spawn.size];
|
||||
var index = 0;
|
||||
foreach (var spawn in pending_spawn.values)
|
||||
result[index++] = spawn;
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool try_resume (uint pid) {
|
||||
HostSpawnInfo? spawn;
|
||||
if (!pending_spawn.unset (pid, out spawn))
|
||||
return false;
|
||||
spawn_removed (spawn);
|
||||
perform_resume.begin (pid);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async void perform_resume (uint pid) {
|
||||
try {
|
||||
yield helper.resume (pid, null);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is Error.INVALID_ARGUMENT)
|
||||
Posix.kill ((Posix.pid_t) pid, Posix.Signal.CONT);
|
||||
}
|
||||
}
|
||||
|
||||
private void process_pending_events () {
|
||||
events_reader.drain (payload => {
|
||||
assert (payload.length == sizeof (ExecveEvent));
|
||||
handle_event (payload);
|
||||
return CONTINUE;
|
||||
});
|
||||
}
|
||||
|
||||
private void handle_event (ExecveEvent * e) {
|
||||
var info = HostSpawnInfo (e->pid, (string) e->command);
|
||||
pending_spawn[e->pid] = info;
|
||||
spawn_added (info);
|
||||
}
|
||||
|
||||
private struct ExecveEvent {
|
||||
public int pid;
|
||||
public char command[MAX_FILENAME];
|
||||
}
|
||||
|
||||
private class EventsWatchState : Object {
|
||||
public unowned SpawnGater gater;
|
||||
|
||||
public EventsWatchState (SpawnGater gater) {
|
||||
this.gater = gater;
|
||||
}
|
||||
|
||||
public bool on_ready (IOChannel ch, IOCondition cond) {
|
||||
gater.process_pending_events ();
|
||||
return Source.CONTINUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,6 +124,11 @@ public sealed class Frida.SyscallTracer : Object {
|
||||
excluded_syscalls.update_u64_u8 (key, 1);
|
||||
}
|
||||
|
||||
public void include_syscall (Abi abi, uint nr) throws Error {
|
||||
uint64 key = ((uint64) abi << 32) | (uint64) nr;
|
||||
excluded_syscalls.remove_u64 (key);
|
||||
}
|
||||
|
||||
public DrainStatus drain_events (SyscallEventHandler on_event) {
|
||||
var status = syscall_events_reader.drain (payload => {
|
||||
assert (payload.length >= sizeof (SyscallEvent));
|
||||
|
||||
@@ -131,11 +131,28 @@ if have_local_backend
|
||||
output: helper_name,
|
||||
command: post_process_helper,
|
||||
build_by_default: true,
|
||||
install: get_option('assets') == 'installed',
|
||||
install: assets_mode == 'installed',
|
||||
install_dir: asset_dir,
|
||||
)
|
||||
|
||||
if get_option('assets') == 'embedded'
|
||||
if assets_mode == 'installed'
|
||||
# frida-gum already has install_data() rules for these, but
|
||||
# releng/meson_make installs with --skip-subprojects when
|
||||
# default_library=shared (to keep other subproject artifacts
|
||||
# like capstone out of the prefix), so re-install them here
|
||||
# alongside the agent / helper where host sessions look for
|
||||
# them at runtime.
|
||||
install_data(
|
||||
gum_dep.get_variable('frida_dbghelp_prefix') / host_abi / 'dbghelp.dll',
|
||||
install_dir: asset_dir,
|
||||
)
|
||||
install_data(
|
||||
gum_dep.get_variable('frida_symsrv_prefix') / host_abi / 'symsrv.dll',
|
||||
install_dir: asset_dir,
|
||||
)
|
||||
endif
|
||||
|
||||
if assets_mode == 'embedded'
|
||||
if helper_modern == 'auto'
|
||||
helper_modern = host_is_modern ? helper : ''
|
||||
endif
|
||||
@@ -256,7 +273,7 @@ if have_local_backend
|
||||
override_options: ['b_asneeded=false'],
|
||||
)
|
||||
|
||||
need_universal_helper = get_option('assets') == 'installed' and helper_compat != ''
|
||||
need_universal_helper = assets_mode == 'installed' and helper_compat != ''
|
||||
|
||||
helper = custom_target('frida-helper',
|
||||
input: [
|
||||
@@ -266,7 +283,7 @@ if have_local_backend
|
||||
output: need_universal_helper ? f'@helper_name@-@host_abi@' : helper_name,
|
||||
command: post_process_helper + '@INPUT1@',
|
||||
build_by_default: true,
|
||||
install: get_option('assets') == 'installed' and not need_universal_helper,
|
||||
install: assets_mode == 'installed' and not need_universal_helper,
|
||||
install_dir: asset_dir,
|
||||
)
|
||||
|
||||
@@ -287,7 +304,7 @@ if have_local_backend
|
||||
)
|
||||
endif
|
||||
|
||||
if get_option('assets') == 'embedded'
|
||||
if assets_mode == 'embedded'
|
||||
if helper_modern == 'auto'
|
||||
helper_modern = host_is_modern ? helper : ''
|
||||
endif
|
||||
@@ -336,6 +353,7 @@ if have_local_backend
|
||||
'linux' / 'frida-helper-backend-glue.c',
|
||||
'linux' / 'frida-helper-backend-types.c',
|
||||
'linux' / 'frida-helper-types.vala',
|
||||
'linux' / 'spawn-gater.vala',
|
||||
'linux' / 'activity-sampler.vala',
|
||||
'linux' / 'syscall-tracer.vala',
|
||||
'linux' / 'symbol-resolver.vala',
|
||||
@@ -368,6 +386,7 @@ if have_local_backend
|
||||
helpers_native_dir / 'bootstrapper.bin',
|
||||
helpers_native_dir / 'loader.bin',
|
||||
helpers_bpf_noarch_dir / 'activity-sampler.elf',
|
||||
helpers_bpf_arch_dir / 'spawn-gater.elf',
|
||||
helpers_bpf_arch_dir / 'syscall-tracer.elf',
|
||||
],
|
||||
output: [
|
||||
@@ -384,6 +403,7 @@ if have_local_backend
|
||||
'@INPUT2@',
|
||||
'@INPUT3@',
|
||||
'@INPUT4@',
|
||||
'@INPUT5@',
|
||||
],
|
||||
)
|
||||
helper_backend_sources += [helper_backend_data]
|
||||
@@ -428,11 +448,11 @@ if have_local_backend
|
||||
output: helper_name,
|
||||
command: post_process_helper,
|
||||
build_by_default: true,
|
||||
install: get_option('assets') == 'installed',
|
||||
install: assets_mode == 'installed',
|
||||
install_dir: asset_dir,
|
||||
)
|
||||
|
||||
if get_option('assets') == 'embedded'
|
||||
if assets_mode == 'embedded'
|
||||
if helper_modern == 'auto'
|
||||
helper_modern = host_is_modern ? helper : ''
|
||||
endif
|
||||
@@ -481,7 +501,7 @@ if have_local_backend
|
||||
]
|
||||
endif
|
||||
|
||||
if get_option('assets') == 'embedded'
|
||||
if assets_mode == 'embedded'
|
||||
if agent_modern == 'auto'
|
||||
agent_modern = host_is_modern ? agent : ''
|
||||
endif
|
||||
@@ -579,10 +599,18 @@ if have_fruity_backend
|
||||
'fruity' / 'kdebug.vala',
|
||||
'fruity' / 'kperfdata.vala',
|
||||
'fruity' / 'cs-signature.vala',
|
||||
'fruity' / 'network-stack.vala',
|
||||
'fruity' / 'usb.vala',
|
||||
'fruity' / 'ncm.vala',
|
||||
]
|
||||
if host_os != 'macos'
|
||||
backend_sources += [
|
||||
'fruity' / 'device-monitor-portable.vala',
|
||||
'fruity' / 'pairing.vala',
|
||||
'fruity' / 'tunnel.vala',
|
||||
'fruity' / 'tls-psk.vala',
|
||||
'fruity' / 'network-stack.vala',
|
||||
'fruity' / 'usb.vala',
|
||||
'fruity' / 'ncm.vala',
|
||||
]
|
||||
endif
|
||||
|
||||
if host_os_family == 'windows'
|
||||
backend_sources += 'fruity' / 'device-monitor-windows.vala'
|
||||
@@ -600,15 +628,17 @@ if have_fruity_backend
|
||||
backend_sources += 'fruity' / 'device-monitor-unix.c'
|
||||
endif
|
||||
|
||||
backend_deps += openssl_dep
|
||||
backend_vala_args += [
|
||||
'--pkg=libnghttp2',
|
||||
'--pkg=libngtcp2',
|
||||
'--pkg=libngtcp2_crypto_quictls',
|
||||
'--pkg=libusb-1.0',
|
||||
'--pkg=lwip',
|
||||
'--pkg=openssl',
|
||||
]
|
||||
backend_vala_args += '--pkg=libnghttp2'
|
||||
if host_os != 'macos'
|
||||
backend_deps += openssl_dep
|
||||
backend_vala_args += [
|
||||
'--pkg=openssl',
|
||||
'--pkg=libngtcp2',
|
||||
ngtcp2_crypto_quictls_dep.found() ? '--pkg=libngtcp2_crypto_quictls' : '--pkg=libngtcp2_crypto_ossl',
|
||||
'--pkg=libusb-1.0',
|
||||
'--pkg=lwip',
|
||||
]
|
||||
endif
|
||||
if host_os_family == 'windows'
|
||||
backend_vala_args += '--pkg=windows-windns'
|
||||
elif host_os_family == 'darwin'
|
||||
@@ -644,10 +674,44 @@ if have_barebone_backend
|
||||
subdir('barebone')
|
||||
endif
|
||||
|
||||
icons_data = custom_target('frida-data-icons',
|
||||
input: [
|
||||
'icons.resources',
|
||||
'icons' / 'local.png',
|
||||
'icons' / 'simmy.png',
|
||||
'icons' / 'fruity-network.png',
|
||||
'icons' / 'droidy.png',
|
||||
'icons' / 'socket.png',
|
||||
'icons' / 'barebone.png',
|
||||
'icons' / 'portal.png',
|
||||
],
|
||||
output: [
|
||||
'frida-data-icons.vapi',
|
||||
'frida-data-icons.h',
|
||||
'frida-data-icons.c',
|
||||
'frida-data-icons-blob' + resource_blob_extension,
|
||||
],
|
||||
command: [
|
||||
resource_compiler_cmd_array,
|
||||
'-c', '@INPUT0@',
|
||||
'-o', meson.current_build_dir() / 'frida-data-icons',
|
||||
'@INPUT1@',
|
||||
'@INPUT2@',
|
||||
'@INPUT3@',
|
||||
'@INPUT4@',
|
||||
'@INPUT5@',
|
||||
'@INPUT6@',
|
||||
'@INPUT7@',
|
||||
],
|
||||
)
|
||||
backend_sources += [icons_data]
|
||||
|
||||
if host_os_family != 'windows'
|
||||
backend_vala_args_private += '--pkg=posix'
|
||||
endif
|
||||
|
||||
core_extra_link_depends = []
|
||||
|
||||
subdir('compiler')
|
||||
|
||||
core_sources = base_sources + backend_sources + compiler_sources
|
||||
@@ -687,8 +751,38 @@ core_vala_args = [
|
||||
|
||||
core_gir_name = f'Frida-@[email protected]'
|
||||
|
||||
core = library('frida-core', core_sources,
|
||||
if is_shared
|
||||
core_lib_name = f'frida-core-@api_version@'
|
||||
else
|
||||
core_lib_name = 'frida-core'
|
||||
endif
|
||||
|
||||
core_extra_link_args = backend_libs_private
|
||||
if is_shared
|
||||
symbol_map_outdir = meson.current_build_dir()
|
||||
run_command(python, files('api' / 'generate.py'),
|
||||
'--output=symbol-maps',
|
||||
symbol_map_outdir,
|
||||
check: true,
|
||||
)
|
||||
if host_os_family == 'windows'
|
||||
if host_toolchain == 'microsoft'
|
||||
foreach sym : ['frida_init', 'frida_init_with_runtime', 'frida_shutdown', 'frida_deinit',
|
||||
'frida_get_main_context', 'frida_unref', 'frida_version', 'frida_version_string']
|
||||
core_extra_link_args += '/EXPORT:' + sym
|
||||
endforeach
|
||||
else
|
||||
core_extra_link_args += '-Wl,--retain-symbols-file,' + symbol_map_outdir / 'frida-core.symbols'
|
||||
endif
|
||||
elif host_os_family != 'darwin'
|
||||
core_extra_link_args += '-Wl,--version-script,' + symbol_map_outdir / 'frida-core.version'
|
||||
endif
|
||||
endif
|
||||
|
||||
core = library(core_lib_name, core_sources,
|
||||
c_args: frida_component_cflags,
|
||||
vala_header: 'frida-core.h',
|
||||
vala_vapi: 'frida-core.vapi',
|
||||
vala_args: [
|
||||
core_vala_args,
|
||||
backend_vala_args_private,
|
||||
@@ -698,17 +792,27 @@ core = library('frida-core', core_sources,
|
||||
'--pkg=libsoup-3.0',
|
||||
],
|
||||
vala_gir: core_gir_name,
|
||||
link_args: backend_libs_private,
|
||||
link_args: core_extra_link_args,
|
||||
link_depends: core_extra_link_depends,
|
||||
link_with: backend_libs,
|
||||
dependencies: core_deps + internal_deps,
|
||||
install: is_shared,
|
||||
)
|
||||
|
||||
core_dep = declare_dependency(
|
||||
include_directories: include_directories('.'),
|
||||
link_args: backend_libs_private,
|
||||
link_with: [core] + backend_libs,
|
||||
dependencies: [gio_dep, gee_dep, json_glib_dep, gum_dep] + backend_deps + internal_deps,
|
||||
)
|
||||
if is_shared
|
||||
core_dep = declare_dependency(
|
||||
include_directories: include_directories('.'),
|
||||
link_with: [core] + backend_libs,
|
||||
dependencies: [gio_dep, gee_dep, json_glib_dep, gum_dep] + backend_deps + internal_deps,
|
||||
)
|
||||
else
|
||||
core_dep = declare_dependency(
|
||||
include_directories: include_directories('.'),
|
||||
link_args: backend_libs_private,
|
||||
link_with: [core] + backend_libs,
|
||||
dependencies: [gio_dep, gee_dep, json_glib_dep, gum_dep] + backend_deps + internal_deps,
|
||||
)
|
||||
endif
|
||||
|
||||
core_build_dir = meson.current_build_dir()
|
||||
subdir('api')
|
||||
|
||||
@@ -1686,7 +1686,7 @@ namespace Frida {
|
||||
packument_cache[name] = packument_promise;
|
||||
}
|
||||
var packument_node = yield packument_promise.future.wait_async (cancellable);
|
||||
var packument_reader = new Json.Reader (packument_node);
|
||||
var packument_reader = make_json_reader_from_node (packument_node);
|
||||
|
||||
string? version_from_dist_tag = null;
|
||||
if (!packument_reader.read_member ("dist-tags"))
|
||||
@@ -2279,7 +2279,7 @@ namespace Frida {
|
||||
|
||||
private async Json.Reader fetch (string resource, Cancellable? cancellable) throws Error, IOError {
|
||||
var root = yield fetch_node (resource, cancellable);
|
||||
return new Json.Reader (root);
|
||||
return make_json_reader_from_node (root);
|
||||
}
|
||||
|
||||
private async Json.Node fetch_node (string resource, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -2560,7 +2560,7 @@ namespace Frida {
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
return new Json.Reader (p.get_root ());
|
||||
return make_json_reader_from_node (p.get_root ());
|
||||
}
|
||||
|
||||
private class SemverVersion {
|
||||
@@ -2672,13 +2672,10 @@ namespace Frida {
|
||||
is_potentially_numeric = false;
|
||||
}
|
||||
|
||||
if (is_potentially_numeric) {
|
||||
try {
|
||||
parse_uint (id);
|
||||
} catch (Error e) {
|
||||
throw new Error.PROTOCOL ("Invalid numeric pre-release identifier '%s' in '%s': %s",
|
||||
id, version, e.message);
|
||||
}
|
||||
if (is_potentially_numeric && id.length > 1 && id[0] == '0') {
|
||||
throw new Error.PROTOCOL (
|
||||
"Numeric pre-release identifier '%s' in '%s' must not have leading zeros",
|
||||
id, version);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2754,10 +2751,12 @@ namespace Frida {
|
||||
bool b_is_num = is_numeric_identifier (id_b);
|
||||
|
||||
if (a_is_num && b_is_num) {
|
||||
var val_a = uint.parse (id_a);
|
||||
var val_b = uint.parse (id_b);
|
||||
if (val_a != val_b)
|
||||
return val_a > val_b ? 1 : -1;
|
||||
int len_cmp = id_a.length - id_b.length;
|
||||
if (len_cmp != 0)
|
||||
return len_cmp;
|
||||
int digit_cmp = strcmp (id_a, id_b);
|
||||
if (digit_cmp != 0)
|
||||
return digit_cmp;
|
||||
} else if (a_is_num) {
|
||||
return -1;
|
||||
} else if (b_is_num) {
|
||||
|
||||
@@ -31,8 +31,8 @@ namespace Frida {
|
||||
|
||||
private State state = STOPPED;
|
||||
|
||||
private WebService cluster_service;
|
||||
private WebService? control_service;
|
||||
private Gee.List<WebService> cluster_services = new Gee.ArrayList<WebService> ();
|
||||
private Gee.List<WebService> control_services = new Gee.ArrayList<WebService> ();
|
||||
|
||||
private Gee.Map<uint, ConnectionEntry> connections = new Gee.HashMap<uint, ConnectionEntry> ();
|
||||
private Gee.MultiMap<string, ConnectionEntry> tags = new Gee.HashMultiMap<string, ConnectionEntry> ();
|
||||
@@ -63,13 +63,9 @@ namespace Frida {
|
||||
construct {
|
||||
_device = new Device (null, new PortalHostSessionProvider (this));
|
||||
|
||||
cluster_service = new WebService (cluster_params, CLUSTER);
|
||||
cluster_service.incoming.connect (on_incoming_cluster_connection);
|
||||
|
||||
if (control_params != null) {
|
||||
control_service = new WebService (control_params, CONTROL);
|
||||
control_service.incoming.connect (on_incoming_control_connection);
|
||||
}
|
||||
cluster_services.add (make_cluster_service (cluster_params));
|
||||
if (control_params != null)
|
||||
control_services.add (make_control_service (control_params));
|
||||
}
|
||||
|
||||
public override void dispose () {
|
||||
@@ -90,6 +86,30 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public void add_cluster_endpoint (EndpointParameters endpoint_params) throws Error {
|
||||
if (state != STOPPED)
|
||||
throw new Error.INVALID_OPERATION ("Cannot add endpoint after service has started");
|
||||
cluster_services.add (make_cluster_service (endpoint_params));
|
||||
}
|
||||
|
||||
public void add_control_endpoint (EndpointParameters endpoint_params) throws Error {
|
||||
if (state != STOPPED)
|
||||
throw new Error.INVALID_OPERATION ("Cannot add endpoint after service has started");
|
||||
control_services.add (make_control_service (endpoint_params));
|
||||
}
|
||||
|
||||
private WebService make_cluster_service (EndpointParameters endpoint_params) {
|
||||
var service = new WebService (endpoint_params, CLUSTER);
|
||||
service.incoming.connect (on_incoming_cluster_connection);
|
||||
return service;
|
||||
}
|
||||
|
||||
private WebService make_control_service (EndpointParameters endpoint_params) {
|
||||
var service = new WebService (endpoint_params, CONTROL);
|
||||
service.incoming.connect (on_incoming_control_connection);
|
||||
return service;
|
||||
}
|
||||
|
||||
public async void start (Cancellable? cancellable = null) throws Error, IOError {
|
||||
if (state != STOPPED)
|
||||
throw new Error.INVALID_OPERATION ("Invalid operation");
|
||||
@@ -98,10 +118,11 @@ namespace Frida {
|
||||
io_cancellable = new Cancellable ();
|
||||
|
||||
try {
|
||||
yield cluster_service.start (cancellable);
|
||||
foreach (var service in cluster_services)
|
||||
yield service.start (cancellable);
|
||||
|
||||
if (control_service != null)
|
||||
yield control_service.start (cancellable);
|
||||
foreach (var service in control_services)
|
||||
yield service.start (cancellable);
|
||||
|
||||
state = STARTED;
|
||||
} catch (GLib.Error e) {
|
||||
@@ -126,10 +147,11 @@ namespace Frida {
|
||||
if (state != STARTED)
|
||||
throw new Error.INVALID_OPERATION ("Invalid operation");
|
||||
|
||||
if (control_service != null)
|
||||
control_service.stop ();
|
||||
foreach (var service in control_services)
|
||||
service.stop ();
|
||||
|
||||
cluster_service.stop ();
|
||||
foreach (var service in cluster_services)
|
||||
service.stop ();
|
||||
|
||||
if (io_cancellable != null)
|
||||
io_cancellable.cancel ();
|
||||
@@ -948,13 +970,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
construct {
|
||||
var builder = new VariantBuilder (VariantType.VARDICT);
|
||||
builder.add ("{sv}", "format", new Variant.string ("rgba"));
|
||||
builder.add ("{sv}", "width", new Variant.uint16 (16));
|
||||
builder.add ("{sv}", "height", new Variant.uint16 (16));
|
||||
var image = new Bytes (Base64.decode ("AAAAAAAAAAAAAAAAOjo6Dzo6OhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOjo6TZCHbvlycnL4Ojo6iTo6OhMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOjo6aa6fdv7878f/+/Te/93d3f9xcXH3Ojo6gTo6Og8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOjo6F4KAfv//5Hn//fHK//r6+v/39/f/9/f3/9LS0v9kZGTzOjo6eDo6OgsAAAAAAAAAAAAAAAAAAAAAAAAAADo6Og6Tk5P/zc3N//z8/P/6+vr/8PDw/+7u7v/p6en/9PT0/8jIyP9XV1f2Ojo6SgAAAAAAAAAAAAAAAAAAAAA6OjoIb29v/8HBwf+5ubn/9/f3/+/v7//p6en/+Pj4/+np6f/o6Oj/4ODg/z09PcsAAAAAAAAAAAAAAAAAAAAAAAAAAjMzM1p8fHz/wsLC/7CwsP/x8fH/8/P0/9zc3f/09PT/+vr6/8vLy/9AQEDFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALS0tV2pqav7BwcH/rq6u/+bm5v/09PT/s7Oz/93d3f/R0dL/VVVVygAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjIyNRWlpa+7+/v/+wsLD/oaGh/4iIiP9NTU7/VVVW/0BAQf89PT61Pj4/BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsbG09NTU32urq6/4yMjP9ycnL/Pj4//1BQUf9tbW7/XFxd/z4+P8M+Pj8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExMTTD09PfBzc3P/LCwsvDAwMbVEREX/f3+A/6ioqf9tbW7zPj4/lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANDQ0vGRkZggAAAAAAAAAAJycnh0NDRP2GhojujIyP4EtLS4k/Pz8YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjIyRoRUVFq21tbp5TU1ZUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkpK10AAAAWAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="));
|
||||
builder.add ("{sv}", "image", Variant.new_from_data (new VariantType ("ay"), image.get_data (), true, image));
|
||||
_icon = builder.end ();
|
||||
_icon = make_provider_icon (Frida.Data.Icons.get_portal_png_blob ().data);
|
||||
}
|
||||
|
||||
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
get { return null; }
|
||||
get { return _icon; }
|
||||
}
|
||||
|
||||
public HostSessionProviderKind kind {
|
||||
@@ -80,8 +80,13 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private static Variant _icon;
|
||||
private SimmyHostSession? host_session;
|
||||
|
||||
static construct {
|
||||
_icon = make_provider_icon (Frida.Data.Icons.get_simmy_png_blob ().data);
|
||||
}
|
||||
|
||||
public SimmyHostSessionProvider (Simmy.Device device) {
|
||||
Object (device: device);
|
||||
}
|
||||
@@ -604,7 +609,7 @@ namespace Frida {
|
||||
Json.Node response = yield call ("fetchApplicationIcons", args, null, cancellable);
|
||||
|
||||
var icons = new Gee.HashMap<string, Bytes> ();
|
||||
var reader = new Json.Reader (response);
|
||||
var reader = make_json_reader_from_node (response);
|
||||
int n = reader.count_elements ();
|
||||
for (int i = 0; i != n; i++) {
|
||||
reader.read_element (i);
|
||||
|
||||
@@ -38,13 +38,7 @@ namespace Frida {
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
construct {
|
||||
var builder = new VariantBuilder (VariantType.VARDICT);
|
||||
builder.add ("{sv}", "format", new Variant.string ("rgba"));
|
||||
builder.add ("{sv}", "width", new Variant.uint16 (16));
|
||||
builder.add ("{sv}", "height", new Variant.uint16 (16));
|
||||
var image = new Bytes (Base64.decode ("AAAAAAAAAAAAAAAAOjo6Dzo6OhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOjo6TZCHbvlycnL4Ojo6iTo6OhMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOjo6aa6fdv7878f/+/Te/93d3f9xcXH3Ojo6gTo6Og8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOjo6F4KAfv//5Hn//fHK//r6+v/39/f/9/f3/9LS0v9kZGTzOjo6eDo6OgsAAAAAAAAAAAAAAAAAAAAAAAAAADo6Og6Tk5P/zc3N//z8/P/6+vr/8PDw/+7u7v/p6en/9PT0/8jIyP9XV1f2Ojo6SgAAAAAAAAAAAAAAAAAAAAA6OjoIb29v/8HBwf+5ubn/9/f3/+/v7//p6en/+Pj4/+np6f/o6Oj/4ODg/z09PcsAAAAAAAAAAAAAAAAAAAAAAAAAAjMzM1p8fHz/wsLC/7CwsP/x8fH/8/P0/9zc3f/09PT/+vr6/8vLy/9AQEDFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALS0tV2pqav7BwcH/rq6u/+bm5v/09PT/s7Oz/93d3f/R0dL/VVVVygAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjIyNRWlpa+7+/v/+wsLD/oaGh/4iIiP9NTU7/VVVW/0BAQf89PT61Pj4/BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsbG09NTU32urq6/4yMjP9ycnL/Pj4//1BQUf9tbW7/XFxd/z4+P8M+Pj8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExMTTD09PfBzc3P/LCwsvDAwMbVEREX/f3+A/6ioqf9tbW7zPj4/lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANDQ0vGRkZggAAAAAAAAAAJycnh0NDRP2GhojujIyP4EtLS4k/Pz8YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjIyRoRUVFq21tbp5TU1ZUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkpK10AAAAWAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="));
|
||||
builder.add ("{sv}", "image", Variant.new_from_data (new VariantType ("ay"), image.get_data (), true, image));
|
||||
_icon = builder.end ();
|
||||
_icon = make_provider_icon (Frida.Data.Icons.get_socket_png_blob ().data);
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace Frida {
|
||||
|
||||
try {
|
||||
string level_str = (level == PrivilegeLevel.ELEVATED) ? "ELEVATED" : "NORMAL";
|
||||
void * process = spawn (resource_store.native_helper.path,
|
||||
void * process = spawn (resource_store.native_helper_path,
|
||||
"MANAGER %s %s".printf (level_str, transport.remote_address),
|
||||
level);
|
||||
instance = new HelperInstance (resource_store.helpers, transport, stream_request, process);
|
||||
@@ -367,7 +367,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private sealed class ResourceStore {
|
||||
public TemporaryFile native_helper {
|
||||
public string native_helper_path {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
@@ -379,25 +379,30 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public ResourceStore (TemporaryDirectory tempdir) throws Error {
|
||||
#if HAVE_EMBEDDED_ASSETS
|
||||
var helper_arm64 = add_helper ("arm64", Frida.Data.Helper.get_frida_helper_arm64_exe_blob (), tempdir);
|
||||
var helper_x86_64 = add_helper ("x86_64", Frida.Data.Helper.get_frida_helper_x86_64_exe_blob (), tempdir);
|
||||
var helper_x86 = add_helper ("x86", Frida.Data.Helper.get_frida_helper_x86_exe_blob (), tempdir);
|
||||
|
||||
switch (Gum.Windows.query_native_cpu_type ()) {
|
||||
case ARM64:
|
||||
native_helper = helper_arm64;
|
||||
native_helper_path = helper_arm64.path;
|
||||
break;
|
||||
case AMD64:
|
||||
native_helper = helper_x86_64;
|
||||
native_helper_path = helper_x86_64.path;
|
||||
break;
|
||||
case IA32:
|
||||
native_helper = helper_x86;
|
||||
native_helper_path = helper_x86.path;
|
||||
break;
|
||||
default:
|
||||
assert_not_reached ();
|
||||
}
|
||||
#else
|
||||
native_helper_path = Frida.helper_path;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if HAVE_EMBEDDED_ASSETS
|
||||
private TemporaryFile add_helper (string name, Frida.Data.Helper.Blob blob, TemporaryDirectory tempdir) throws Error {
|
||||
var file = new TemporaryFile.from_stream (@"frida-helper-$name.exe",
|
||||
new MemoryInputStream.from_data (blob.data, null),
|
||||
@@ -405,5 +410,6 @@ namespace Frida {
|
||||
helpers.add (file);
|
||||
return file;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ namespace Frida {
|
||||
injector = new Winjector (helper, false, tempdir);
|
||||
injector.uninjected.connect (on_uninjected);
|
||||
|
||||
#if HAVE_EMBEDDED_ASSETS
|
||||
agent = new AgentDescriptor (PathTemplate ("<arch>\\frida-agent.dll"),
|
||||
new Bytes.static (Frida.Data.Agent.get_frida_agent_arm64_dll_blob ().data),
|
||||
new Bytes.static (Frida.Data.Agent.get_frida_agent_x86_64_dll_blob ().data),
|
||||
@@ -66,8 +67,28 @@ namespace Frida {
|
||||
},
|
||||
tempdir
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !HAVE_EMBEDDED_ASSETS
|
||||
private static PathTemplate installed_agent_path_template () {
|
||||
return PathTemplate (Frida.agent_path);
|
||||
}
|
||||
|
||||
private static string[] installed_agent_dependencies () {
|
||||
string lib_root = Path.get_dirname (Path.get_dirname (Frida.agent_path));
|
||||
string[] deps = {};
|
||||
foreach (unowned string arch in new string[] { "arm64", "x86_64", "x86" }) {
|
||||
foreach (unowned string name in new string[] { "dbghelp.dll", "symsrv.dll" }) {
|
||||
string p = Path.build_filename (lib_root, arch, name);
|
||||
if (FileUtils.test (p, EXISTS))
|
||||
deps += p;
|
||||
}
|
||||
}
|
||||
return deps;
|
||||
}
|
||||
#endif
|
||||
|
||||
public override async void close (Cancellable? cancellable) throws IOError {
|
||||
yield base.close (cancellable);
|
||||
|
||||
@@ -98,7 +119,11 @@ namespace Frida {
|
||||
|
||||
protected override async AgentSessionProvider create_system_session_provider (Cancellable? cancellable,
|
||||
out DBusConnection connection) throws Error, IOError {
|
||||
#if HAVE_EMBEDDED_ASSETS
|
||||
var path_template = agent.get_path_template ();
|
||||
#else
|
||||
var path_template = installed_agent_path_template ();
|
||||
#endif
|
||||
|
||||
unowned string arch;
|
||||
switch (Gum.NATIVE_CPU) {
|
||||
@@ -225,8 +250,15 @@ namespace Frida {
|
||||
var stream_request = Pipe.open (t.local_address, cancellable);
|
||||
|
||||
var winjector = injector as Winjector;
|
||||
#if HAVE_EMBEDDED_ASSETS
|
||||
var id = yield winjector.inject_library_resource (pid, agent, "frida_agent_main",
|
||||
make_agent_parameters (pid, t.remote_address, options), cancellable);
|
||||
#else
|
||||
var id = yield winjector.inject_library_file_with_template (pid,
|
||||
installed_agent_path_template (), "frida_agent_main",
|
||||
make_agent_parameters (pid, t.remote_address, options),
|
||||
installed_agent_dependencies (), cancellable);
|
||||
#endif
|
||||
injectee_by_pid[pid] = id;
|
||||
|
||||
transport = t;
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Frida {
|
||||
cancellable);
|
||||
}
|
||||
|
||||
private async uint inject_library_file_with_template (uint pid, PathTemplate path_template, string entrypoint, string data,
|
||||
public async uint inject_library_file_with_template (uint pid, PathTemplate path_template, string entrypoint, string data,
|
||||
string[] dependencies, Cancellable? cancellable) throws Error, IOError {
|
||||
uint id = next_injectee_id++;
|
||||
yield helper.inject_library_file (pid, path_template, entrypoint, data, dependencies, id, cancellable);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/frida-gum.git
|
||||
revision = 17.8.2
|
||||
revision = 17.9.2
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/libnice.git
|
||||
revision = e12567b0a16a0c2eb5dfe5e0782baba8496772ff
|
||||
revision = 9a3da6e3e5bbcf935fd85b6b8557ff8c7dd9032c
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/nghttp2.git
|
||||
revision = ae13d24ea59c30e36ca53d1b22c4e664588d0445
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
dependency_names = libnghttp2
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/ngtcp2.git
|
||||
revision = 828dcaed498b40954e1b496664a3309796968db6
|
||||
revision = 5b21d4418ff668d9b5ddc4f744b7a28af4196abf
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/usrsctp.git
|
||||
revision = f459ae9d3700c06e59d709901e92c08e31c6e623
|
||||
revision = 377fecfe06bb766c69cc0e1e1f6d5c72cf7704e6
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -23,7 +23,9 @@ endif
|
||||
if build_gadget
|
||||
test_vala_args += '--define=HAVE_GADGET'
|
||||
test_sources += 'test-gadget.vala'
|
||||
test_depends += gadget
|
||||
if not have_shared_glib
|
||||
test_depends += gadget
|
||||
endif
|
||||
endif
|
||||
|
||||
subdir('labrats')
|
||||
@@ -68,12 +70,17 @@ if host_os == 'macos'
|
||||
extra_link_depends += [runner_plist]
|
||||
endif
|
||||
|
||||
runner_deps = [core_dep]
|
||||
if host_os_family == 'darwin'
|
||||
runner_deps += dependency('appleframeworks', modules: ['Foundation'])
|
||||
endif
|
||||
|
||||
raw_runner = executable('frida-tests-raw', 'main.vala',
|
||||
vala_args: [core_vala_args, system_vala_args],
|
||||
link_args: executable_no_exports_link_args + extra_link_args,
|
||||
link_depends: executable_no_exports_link_depends + extra_link_depends,
|
||||
link_with: frida_tests,
|
||||
dependencies: [core_dep],
|
||||
dependencies: runner_deps,
|
||||
)
|
||||
|
||||
runner = custom_target('frida-tests',
|
||||
|
||||
@@ -234,7 +234,7 @@ Interceptor.attach(Process.getModuleByName('/usr/lib/system/libsystem_kernel.dyl
|
||||
while (true) {
|
||||
var message = yield h.wait_for_message ();
|
||||
|
||||
var reader = new Json.Reader (Json.from_string (message.text));
|
||||
var reader = make_json_reader (message.text);
|
||||
|
||||
reader.read_member ("type");
|
||||
if (reader.get_string_value () != "send") {
|
||||
@@ -278,7 +278,7 @@ Interceptor.attach(Process.getModuleByName('/usr/lib/system/libsystem_kernel.dyl
|
||||
var message = yield h.wait_for_message ();
|
||||
printerr ("got message: %s\n", message.text);
|
||||
|
||||
var reader = new Json.Reader (Json.from_string (message.text));
|
||||
var reader = make_json_reader (message.text);
|
||||
|
||||
reader.read_member ("type");
|
||||
if (reader.get_string_value () != "send") {
|
||||
|
||||
@@ -1218,15 +1218,13 @@ namespace Frida.HostSessionTest {
|
||||
}
|
||||
""");
|
||||
var message_handler = script.message.connect ((json, data) => {
|
||||
var parser = new Json.Parser ();
|
||||
Json.Reader reader;
|
||||
try {
|
||||
parser.load_from_data (json);
|
||||
reader = make_json_reader (json);
|
||||
} catch (GLib.Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
var reader = new Json.Reader (parser.get_root ());
|
||||
|
||||
assert (reader.read_member ("type") && reader.get_string_value () == "send");
|
||||
reader.end_member ();
|
||||
|
||||
@@ -1477,15 +1475,13 @@ namespace Frida.HostSessionTest {
|
||||
}
|
||||
""");
|
||||
var message_handler = script.message.connect ((json, data) => {
|
||||
var parser = new Json.Parser ();
|
||||
Json.Reader reader;
|
||||
try {
|
||||
parser.load_from_data (json);
|
||||
reader = make_json_reader (json);
|
||||
} catch (GLib.Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
var reader = new Json.Reader (parser.get_root ());
|
||||
|
||||
assert (reader.read_member ("type") && reader.get_string_value () == "send");
|
||||
reader.end_member ();
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
@@ -51,32 +50,37 @@ def main(argv: List[str]):
|
||||
args = argv[1:]
|
||||
go = Path(args.pop(0))
|
||||
host_os = args.pop(0)
|
||||
host_os_family = args.pop(0)
|
||||
host_abi = args.pop(0)
|
||||
libc = args.pop(0)
|
||||
exe_suffix = args.pop(0)
|
||||
shlib_suffix = args.pop(0)
|
||||
cc_id = args.pop(0)
|
||||
assets_mode = args.pop(0)
|
||||
cc_cmd_array = pop_cmd_array_arg(args)
|
||||
ar_cmd_array = pop_cmd_array_arg(args)
|
||||
nm_cmd_array = pop_cmd_array_arg(args)
|
||||
ranlib_cmd_array = pop_cmd_array_arg(args)
|
||||
|
||||
try:
|
||||
config = detect_config(
|
||||
mode, config_path = detect_config(
|
||||
go,
|
||||
host_os,
|
||||
host_os_family,
|
||||
host_abi,
|
||||
libc,
|
||||
exe_suffix,
|
||||
shlib_suffix,
|
||||
cc_id,
|
||||
assets_mode,
|
||||
cc_cmd_array,
|
||||
ar_cmd_array,
|
||||
nm_cmd_array,
|
||||
ranlib_cmd_array,
|
||||
)
|
||||
print("ok")
|
||||
print(config["mode"], base64.b64encode(json.dumps(config).encode("utf-8")).decode("ascii"))
|
||||
print(mode)
|
||||
print(config_path)
|
||||
except Exception as e:
|
||||
print("error")
|
||||
print(str(e).replace("\n", "\\n"))
|
||||
@@ -99,16 +103,18 @@ def pop_cmd_array_arg(args: List[str]) -> List[str]:
|
||||
def detect_config(
|
||||
go: Path,
|
||||
host_os: str,
|
||||
host_os_family: str,
|
||||
host_abi: str,
|
||||
libc: str,
|
||||
exe_suffix: str,
|
||||
shlib_suffix: str,
|
||||
cc_id: str,
|
||||
assets_mode: str,
|
||||
cc_cmd_array: List[str],
|
||||
ar_cmd_array: Optional[List[str]],
|
||||
nm_cmd_array: Optional[List[str]],
|
||||
ranlib_cmd_array: Optional[List[str]],
|
||||
) -> dict:
|
||||
) -> Tuple[str, Path]:
|
||||
source_root = Path(os.environ["MESON_SOURCE_ROOT"])
|
||||
build_root = Path(os.environ["MESON_BUILD_ROOT"])
|
||||
subdir = Path(os.environ["MESON_SUBDIR"])
|
||||
@@ -169,9 +175,12 @@ def detect_config(
|
||||
env["GOOS"] = goos
|
||||
env["GOARCH"] = goarch
|
||||
|
||||
if libc == "musl":
|
||||
apple_non_macos = host_os_family == "darwin" and host_os != "macos"
|
||||
if apple_non_macos:
|
||||
mode = "c-archive"
|
||||
elif libc == "musl":
|
||||
mode = "pie"
|
||||
elif host_os == "android":
|
||||
elif host_os == "android" or assets_mode == "installed":
|
||||
mode = "c-shared"
|
||||
else:
|
||||
mode = "c-archive"
|
||||
@@ -187,6 +196,7 @@ def detect_config(
|
||||
config = {
|
||||
"mode": mode,
|
||||
"os": host_os,
|
||||
"os_family": goos,
|
||||
"abi": host_abi,
|
||||
"exe_suffix": exe_suffix,
|
||||
"shlib_suffix": shlib_suffix,
|
||||
@@ -198,7 +208,11 @@ def detect_config(
|
||||
}
|
||||
if mingw is not None:
|
||||
config["mingw"] = mingw
|
||||
return config
|
||||
|
||||
config_path = work_dir / "go-config.json"
|
||||
config_path.write_text(json.dumps(config), encoding="utf-8")
|
||||
|
||||
return config["mode"], config_path
|
||||
|
||||
|
||||
class BuildError(Exception):
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Regenerate the backend provider icons bundled by the resource compiler.
|
||||
|
||||
Requires `rsvg-convert` (librsvg) on PATH. The icons use accent colors
|
||||
drawn from SF Symbols and libadwaita so they feel native in both
|
||||
SwiftUI and GTK/Adwaita consumers. Each icon is rendered to a 96x96
|
||||
PNG under src/icons/, where `icons.resources` picks it up.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main() -> None:
|
||||
ICONS_DIR.mkdir(exist_ok=True)
|
||||
for name, svg in icons():
|
||||
png_path = ICONS_DIR / f"{name}.png"
|
||||
render_png(svg, ICON_SIZE, png_path)
|
||||
print(f"updated {png_path.relative_to(ROOT)}")
|
||||
|
||||
|
||||
def icons() -> list[tuple[str, str]]:
|
||||
return [
|
||||
("local", svg_local()),
|
||||
("simmy", svg_simmy()),
|
||||
("fruity-network", svg_fruity_network()),
|
||||
("droidy", svg_droidy()),
|
||||
("socket", svg_socket()),
|
||||
("barebone", svg_barebone()),
|
||||
("portal", svg_portal()),
|
||||
]
|
||||
|
||||
|
||||
def svg_local() -> str:
|
||||
return f"""\
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<linearGradient id="case" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="{SLATE_LIGHT}"/>
|
||||
<stop offset="1" stop-color="{SLATE_DARK}"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="6" y="10" width="52" height="36" rx="3.5" fill="url(#case)"/>
|
||||
<rect x="9" y="13" width="46" height="30" rx="1.8" fill="{SCREEN}"/>
|
||||
<path d="M 17 22 L 23 28 L 17 34" stroke="#FFFFFF" stroke-width="2.8"
|
||||
stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<rect x="27" y="31" width="10" height="2.8" rx="0.7" fill="#FFFFFF" opacity="0.85"/>
|
||||
<rect x="28" y="46" width="8" height="5" fill="url(#case)"/>
|
||||
<rect x="16" y="51" width="32" height="4" rx="2" fill="url(#case)"/>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
|
||||
def svg_simmy() -> str:
|
||||
return f"""\
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<linearGradient id="phone" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="{CHROME_LIGHT}"/>
|
||||
<stop offset="1" stop-color="{CHROME_DARK}"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="screen" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="{SIM_SCREEN_TOP}"/>
|
||||
<stop offset="1" stop-color="{SIM_SCREEN_BOTTOM}"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="21" y="6" width="22" height="52" rx="5" fill="url(#phone)"/>
|
||||
<rect x="22.5" y="10" width="19" height="42" rx="2.5" fill="url(#screen)"/>
|
||||
<circle cx="27.5" cy="14.5" r="1.6" fill="{TL_RED}"/>
|
||||
<circle cx="32" cy="14.5" r="1.6" fill="{TL_YELLOW}"/>
|
||||
<circle cx="36.5" cy="14.5" r="1.6" fill="{TL_GREEN}"/>
|
||||
<rect x="29" y="54" width="6" height="1.3" rx="0.65" fill="{CHROME_DARK}" opacity="0.7"/>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
|
||||
def svg_fruity_network() -> str:
|
||||
return f"""\
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<linearGradient id="phone" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="{SLATE_LIGHT}"/>
|
||||
<stop offset="1" stop-color="{SLATE_DARK}"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="wave" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="{BLUE_LIGHT}"/>
|
||||
<stop offset="1" stop-color="{BLUE}"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g stroke="url(#wave)" stroke-linecap="round" fill="none">
|
||||
<path d="M 11 22 Q 32 2 53 22" stroke-width="3.2" opacity="0.45"/>
|
||||
<path d="M 17 24 Q 32 11 47 24" stroke-width="3.2" opacity="0.75"/>
|
||||
<path d="M 23 26 Q 32 18 41 26" stroke-width="3.2" opacity="1.00"/>
|
||||
</g>
|
||||
<rect x="23.5" y="28" width="17" height="34" rx="4.5" fill="url(#phone)"/>
|
||||
<rect x="25" y="29.5" width="14" height="31" rx="3.2" fill="{SCREEN}"/>
|
||||
<rect x="28.5" y="31" width="7" height="2.4" rx="1.2" fill="{ISLAND}"/>
|
||||
<rect x="29" y="58" width="6" height="1.3" rx="0.65" fill="{DETAIL}" opacity="0.8"/>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
|
||||
def svg_droidy() -> str:
|
||||
return f"""\
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="{GREEN_LIGHT}"/>
|
||||
<stop offset="1" stop-color="{GREEN_DARK}"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path d="M 21 16 L 17 9" stroke="{GREEN_DARK}" stroke-width="3" stroke-linecap="round" fill="none"/>
|
||||
<path d="M 43 16 L 47 9" stroke="{GREEN_DARK}" stroke-width="3" stroke-linecap="round" fill="none"/>
|
||||
<path d="M 11 26 Q 11 12 32 12 Q 53 12 53 26 L 53 47 Q 53 51 49 51 L 15 51 Q 11 51 11 47 Z"
|
||||
fill="url(#g)"/>
|
||||
<circle cx="23" cy="30" r="3.2" fill="{GREEN_EYE}"/>
|
||||
<circle cx="41" cy="30" r="3.2" fill="{GREEN_EYE}"/>
|
||||
<path d="M 14 22 Q 20 14 32 14" stroke="#FFFFFF" stroke-width="1.6"
|
||||
stroke-linecap="round" fill="none" opacity="0.40"/>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
|
||||
def svg_socket() -> str:
|
||||
return f"""\
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<radialGradient id="g" cx="0.35" cy="0.30" r="0.95">
|
||||
<stop offset="0" stop-color="{BLUE_LIGHT}"/>
|
||||
<stop offset="0.55" stop-color="{BLUE}"/>
|
||||
<stop offset="1" stop-color="{BLUE_DARK}"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<circle cx="32" cy="32" r="25" fill="url(#g)"/>
|
||||
<g fill="none" stroke="#FFFFFF" stroke-width="1.6" opacity="0.92" stroke-linecap="round">
|
||||
<ellipse cx="32" cy="32" rx="25" ry="10"/>
|
||||
<ellipse cx="32" cy="32" rx="10" ry="25"/>
|
||||
<ellipse cx="32" cy="32" rx="20" ry="25" opacity="0.55"/>
|
||||
<ellipse cx="32" cy="32" rx="25" ry="20" opacity="0.55"/>
|
||||
<line x1="7" y1="32" x2="57" y2="32"/>
|
||||
</g>
|
||||
<circle cx="32" cy="32" r="25" fill="none" stroke="{BLUE_DARK}" stroke-width="1" opacity="0.35"/>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
|
||||
def svg_barebone() -> str:
|
||||
return f"""\
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<linearGradient id="chip" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="{CHIP_LIGHT}"/>
|
||||
<stop offset="1" stop-color="{CHIP_DARK}"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="pin" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="{PIN_LIGHT}"/>
|
||||
<stop offset="1" stop-color="{PIN_DARK}"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="led" cx="0.5" cy="0.5" r="0.5">
|
||||
<stop offset="0" stop-color="{LED_BRIGHT}"/>
|
||||
<stop offset="0.55" stop-color="{LED}"/>
|
||||
<stop offset="1" stop-color="{LED_DARK}"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<g fill="url(#pin)">
|
||||
<rect x="3" y="19" width="9" height="4" rx="1"/>
|
||||
<rect x="3" y="30" width="9" height="4" rx="1"/>
|
||||
<rect x="3" y="41" width="9" height="4" rx="1"/>
|
||||
<rect x="52" y="19" width="9" height="4" rx="1"/>
|
||||
<rect x="52" y="30" width="9" height="4" rx="1"/>
|
||||
<rect x="52" y="41" width="9" height="4" rx="1"/>
|
||||
</g>
|
||||
<rect x="12" y="12" width="40" height="40" rx="3.5" fill="url(#chip)"/>
|
||||
<circle cx="32" cy="12" r="3" fill="{NOTCH}"/>
|
||||
<circle cx="32" cy="36" r="5" fill="url(#led)"/>
|
||||
<circle cx="32" cy="36" r="1.6" fill="#FFF7DC"/>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
|
||||
def svg_portal() -> str:
|
||||
return f"""\
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<radialGradient id="hub" cx="0.40" cy="0.35" r="0.85">
|
||||
<stop offset="0" stop-color="{PURPLE_LIGHT}"/>
|
||||
<stop offset="0.65" stop-color="{PURPLE}"/>
|
||||
<stop offset="1" stop-color="{PURPLE_DARK}"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="node" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="{TEAL_LIGHT}"/>
|
||||
<stop offset="1" stop-color="{TEAL_DARK}"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g stroke="{TEAL}" stroke-width="3" stroke-linecap="round" opacity="0.85">
|
||||
<line x1="32" y1="32" x2="13" y2="13"/>
|
||||
<line x1="32" y1="32" x2="51" y2="13"/>
|
||||
<line x1="32" y1="32" x2="13" y2="51"/>
|
||||
<line x1="32" y1="32" x2="51" y2="51"/>
|
||||
</g>
|
||||
<circle cx="13" cy="13" r="6.5" fill="url(#node)"/>
|
||||
<circle cx="51" cy="13" r="6.5" fill="url(#node)"/>
|
||||
<circle cx="13" cy="51" r="6.5" fill="url(#node)"/>
|
||||
<circle cx="51" cy="51" r="6.5" fill="url(#node)"/>
|
||||
<circle cx="32" cy="32" r="11.5" fill="url(#hub)"/>
|
||||
<circle cx="32" cy="32" r="4.5" fill="#FFFFFF" opacity="0.95"/>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
|
||||
def render_png(svg: str, size: int, out: Path) -> None:
|
||||
subprocess.run(
|
||||
["rsvg-convert", "-w", str(size), "-h", str(size), "-o", str(out)],
|
||||
input=svg.encode("utf-8"),
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
ICON_SIZE = 96
|
||||
ICONS_DIR = ROOT / "src" / "icons"
|
||||
|
||||
BLUE = "#2E77E4"
|
||||
BLUE_LIGHT = "#5AAEF5"
|
||||
BLUE_DARK = "#1A5FB4"
|
||||
|
||||
CHROME_LIGHT = "#EFEEF1"
|
||||
CHROME_DARK = "#9A9996"
|
||||
SIM_SCREEN_TOP = "#3D3846"
|
||||
SIM_SCREEN_BOTTOM = "#1F1B2B"
|
||||
TL_RED = "#FF5F56"
|
||||
TL_YELLOW = "#FFBD2E"
|
||||
TL_GREEN = "#27C93F"
|
||||
|
||||
SLATE_LIGHT = "#8B8A95"
|
||||
SLATE_DARK = "#4E4C58"
|
||||
SCREEN = "#17131F"
|
||||
ISLAND = "#050208"
|
||||
DETAIL = "#DCDADF"
|
||||
|
||||
GREEN_LIGHT = "#57E389"
|
||||
GREEN_DARK = "#26A269"
|
||||
GREEN_EYE = "#0F3B20"
|
||||
|
||||
CHIP_LIGHT = "#4E4C58"
|
||||
CHIP_DARK = "#1F1B2B"
|
||||
PIN_LIGHT = "#B5B3BB"
|
||||
PIN_DARK = "#6E6D75"
|
||||
NOTCH = "#0F0B17"
|
||||
LED_BRIGHT = "#FFD89B"
|
||||
LED = "#FFA348"
|
||||
LED_DARK = "#C65B10"
|
||||
|
||||
PURPLE = "#9141AC"
|
||||
PURPLE_LIGHT = "#DC8ADD"
|
||||
PURPLE_DARK = "#613583"
|
||||
|
||||
TEAL = "#33C7DE"
|
||||
TEAL_LIGHT = "#5EDDEE"
|
||||
TEAL_DARK = "#1D8DA8"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -19,7 +19,7 @@ if [ ! -f "$executable" ]; then
|
||||
exit 4
|
||||
fi
|
||||
|
||||
agent=$prefix/usr/lib/frida/frida-agent.dylib
|
||||
agent=$prefix/usr/lib/frida-1.0/frida-agent.dylib
|
||||
if [ ! -f "$agent" ]; then
|
||||
echo "$agent: not found" > /dev/stderr
|
||||
exit 5
|
||||
@@ -41,7 +41,7 @@ tmpdir="$(mktemp -d /tmp/package-server.XXXXXX)"
|
||||
|
||||
pkroot=$tmpdir$sysroot
|
||||
bindir=$pkroot/usr/sbin
|
||||
libdir=$pkroot/usr/lib/frida
|
||||
libdir=$pkroot/usr/lib/frida-1.0
|
||||
daedir=$pkroot/Library/LaunchDaemons
|
||||
|
||||
mkdir -p "$bindir/"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
[CCode (lower_case_cprefix = "")]
|
||||
namespace Config {
|
||||
public const string FRIDA_PREFIX;
|
||||
public const string FRIDA_HELPER_PATH;
|
||||
public const string FRIDA_AGENT_PATH;
|
||||
public const string FRIDA_LIBDIR_NAME;
|
||||
public const string FRIDA_HELPER_NAME;
|
||||
public const string FRIDA_AGENT_NAME;
|
||||
public const string FRIDA_COMPILER_BACKEND_NAME;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
[CCode (cheader_filename = "ngtcp2/ngtcp2_crypto.h", lower_case_cprefix = "ngtcp2_crypto_", gir_namespace = "NGTcp2Crypto", gir_version = "1.0")]
|
||||
namespace NGTcp2.Crypto {
|
||||
[CCode (cheader_filename = "ngtcp2/ngtcp2_crypto_ossl.h")]
|
||||
namespace Ossl {
|
||||
public int configure_client_session (OpenSSL.SSL ssl);
|
||||
}
|
||||
|
||||
[CCode (cname = "ngtcp2_crypto_conn_ref", has_destroy_function = false)]
|
||||
public struct ConnectionRef {
|
||||
public GetConnection get_conn;
|
||||
public void * user_data;
|
||||
}
|
||||
|
||||
[CCode (cname = "ngtcp2_crypto_get_conn", has_target = false)]
|
||||
public delegate unowned Connection GetConnection (ConnectionRef conn_ref);
|
||||
|
||||
public ClientInitial client_initial_cb;
|
||||
public RecvCryptoData recv_crypto_data_cb;
|
||||
public Encrypt encrypt_cb;
|
||||
public Decrypt decrypt_cb;
|
||||
public HpMask hp_mask_cb;
|
||||
public RecvRetry recv_retry_cb;
|
||||
public UpdateKey update_key_cb;
|
||||
public DeleteCryptoAeadCtx delete_crypto_aead_ctx_cb;
|
||||
public DeleteCryptoCipherCtx delete_crypto_cipher_ctx_cb;
|
||||
public GetPathChallengeData get_path_challenge_data_cb;
|
||||
public VersionNegotiation version_negotiation_cb;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
[CCode (lower_case_cprefix = "OPENSSL_", gir_namespace = "OpenSSL", gir_version = "1.0")]
|
||||
namespace OpenSSL {
|
||||
[Compact]
|
||||
[CCode (cname = "SSL_CTX", cprefix = "SSL_CTX_")]
|
||||
[CCode (cheader_filename = "openssl/ssl.h", cname = "SSL_CTX", cprefix = "SSL_CTX_")]
|
||||
public class SSLContext {
|
||||
public SSLContext (SSLMethod meth);
|
||||
|
||||
@@ -12,10 +12,11 @@ namespace OpenSSL {
|
||||
}
|
||||
|
||||
[Compact]
|
||||
[CCode (cname = "SSL", cprefix = "SSL_")]
|
||||
[CCode (cheader_filename = "openssl/ssl.h", cname = "SSL", cprefix = "SSL_")]
|
||||
public class SSL {
|
||||
public SSL (SSLContext ctx);
|
||||
|
||||
public void * get_app_data ();
|
||||
public void set_app_data (void * data);
|
||||
|
||||
public void set_connect_state ();
|
||||
@@ -26,13 +27,35 @@ namespace OpenSSL {
|
||||
public void set_psk_client_callback (PskClientCallback callback);
|
||||
|
||||
public void set_quic_transport_version (int version);
|
||||
|
||||
public void set_bio (void * rbio, void * wbio);
|
||||
|
||||
public int do_handshake ();
|
||||
public int read (uint8[] buf);
|
||||
public int write (uint8[] buf);
|
||||
public int shutdown ();
|
||||
public int get_error (int ret);
|
||||
public int pending ();
|
||||
}
|
||||
|
||||
[CCode (cheader_filename = "openssl/ssl.h", cname = "int", cprefix = "SSL_ERROR_", has_type_id = false)]
|
||||
public enum SSLErrorCode {
|
||||
NONE,
|
||||
SSL,
|
||||
WANT_READ,
|
||||
WANT_WRITE,
|
||||
WANT_X509_LOOKUP,
|
||||
SYSCALL,
|
||||
ZERO_RETURN,
|
||||
WANT_CONNECT,
|
||||
WANT_ACCEPT,
|
||||
}
|
||||
|
||||
[CCode (has_target = false)]
|
||||
public delegate uint PskClientCallback (SSL ssl, string? hint, char[] identity, uint8[] psk);
|
||||
|
||||
[Compact]
|
||||
[CCode (cname = "SSL_METHOD", cprefix = "SSL_METHOD_", free_function = "")]
|
||||
[CCode (cheader_filename = "openssl/ssl.h", cname = "SSL_METHOD", cprefix = "SSL_METHOD_", free_function = "")]
|
||||
public class SSLMethod {
|
||||
[CCode (cname = "TLS_method")]
|
||||
public static unowned SSLMethod tls ();
|
||||
@@ -108,6 +131,23 @@ namespace OpenSSL {
|
||||
public BasicIO.from_static_memory_buffer (uint8[] buf);
|
||||
|
||||
public long get_mem_data ([CCode (array_length = false)] out unowned uint8[] data);
|
||||
|
||||
public int read (uint8[] buf);
|
||||
public int write (uint8[] buf);
|
||||
public size_t ctrl_pending ();
|
||||
|
||||
[CCode (cname = "BIO_new")]
|
||||
public static void * raw_new (BasicIOMethod method);
|
||||
[CCode (cname = "BIO_free")]
|
||||
public static int raw_free (void * bio);
|
||||
[CCode (cname = "BIO_read")]
|
||||
public static int raw_read (void * bio, uint8[] buf);
|
||||
[CCode (cname = "BIO_write")]
|
||||
public static int raw_write (void * bio, uint8[] buf);
|
||||
[CCode (cname = "BIO_ctrl_pending")]
|
||||
public static size_t raw_ctrl_pending (void * bio);
|
||||
[CCode (cname = "BIO_get_mem_data")]
|
||||
public static long raw_get_mem_data (void * bio, [CCode (array_length = false)] out unowned uint8[] data);
|
||||
}
|
||||
|
||||
[Compact]
|
||||
@@ -162,7 +202,7 @@ namespace OpenSSL {
|
||||
[Compact]
|
||||
[CCode (cname = "EVP_PKEY", cprefix = "EVP_PKEY_", copy_function = "EVP_PKEY_dup")]
|
||||
public class Key {
|
||||
[CCode (cname = "d2i_PUBKEY_bio")]
|
||||
[CCode (cheader_filename = "openssl/x509.h", cname = "d2i_PUBKEY_bio")]
|
||||
public Key.from_der (BasicIO source, Key ** a = null);
|
||||
[CCode (cname = "EVP_PKEY_new_raw_public_key")]
|
||||
public Key.from_raw_public_key (KeyType type, Engine? engine, uint8[] pub);
|
||||
@@ -172,7 +212,7 @@ namespace OpenSSL {
|
||||
public int get_raw_public_key ([CCode (array_length = false)] uint8[]? pub, ref size_t len);
|
||||
public int get_raw_private_key ([CCode (array_length = false)] uint8[]? priv, ref size_t len);
|
||||
|
||||
[CCode (cname = "i2d_PUBKEY_bio", instance_pos = 2)]
|
||||
[CCode (cheader_filename = "openssl/x509.h", cname = "i2d_PUBKEY_bio", instance_pos = 2)]
|
||||
public int to_der (BasicIO sink);
|
||||
|
||||
[CCode (cname = "PEM_write_bio_PUBKEY", instance_pos = 2)]
|
||||
@@ -534,7 +574,7 @@ namespace OpenSSL {
|
||||
public int memcmp (void * a, void * b, size_t len);
|
||||
}
|
||||
|
||||
[CCode (lower_case_cprefix = "ERR_")]
|
||||
[CCode (cheader_filename = "openssl/err.h", lower_case_cprefix = "ERR_")]
|
||||
namespace Error {
|
||||
public ulong get_error ();
|
||||
public void error_string_n (ulong e, char[] buf);
|
||||
|
||||