mirror of
https://github.com/frida/frida-core.git
synced 2026-11-09 01:59:34 -10:00
Compare commits
No files matched your search
+221
-14
@@ -16,13 +16,13 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- { id: windows-x86_64, runner: '"windows-latest"' }
|
||||
- { id: windows-x86, runner: '"windows-latest"' }
|
||||
- { id: macos-x86_64, runner: '"macos-latest"' }
|
||||
- { id: macos-arm64, runner: '"macos-latest"' }
|
||||
- { id: linux-x86_64, runner: '"ubuntu-latest"' }
|
||||
- { id: linux-x86, runner: '"ubuntu-latest"' }
|
||||
- { id: freebsd-arm64, runner: '["self-hosted", "freebsd", "arm64"]' }
|
||||
- { id: windows-arm64, runner: '"windows-11-arm"', sys: CLANGARM64, pkg: 'mingw-w64-clang-aarch64-clang' }
|
||||
- { id: windows-x86_64, runner: '"windows-latest"', sys: MINGW64, pkg: 'mingw-w64-x86_64-gcc' }
|
||||
- { id: windows-x86, runner: '"windows-latest"', sys: MINGW32, pkg: 'mingw-w64-i686-gcc' }
|
||||
- { id: macos-x86_64, runner: '"macos-latest"' }
|
||||
- { id: macos-arm64, runner: '"macos-latest"' }
|
||||
- { id: linux-x86_64, runner: '"ubuntu-latest"' }
|
||||
- { id: linux-x86, runner: '"ubuntu-latest"' }
|
||||
fail-fast: false
|
||||
runs-on: ${{ fromJSON(matrix.runner) }}
|
||||
steps:
|
||||
@@ -30,6 +30,18 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install MinGW toolchain for cgo support
|
||||
if: ${{ startsWith(matrix.id, 'windows-') }}
|
||||
id: msys2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ matrix.sys }}
|
||||
install: ${{ matrix.pkg }}
|
||||
- name: Install Go toolchain
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'src/compiler/go.mod'
|
||||
cache-dependency-path: 'src/compiler/go.mod'
|
||||
- name: Set up Apple certificates
|
||||
if: ${{ startsWith(matrix.id, 'macos-') }}
|
||||
uses: ./.github/actions/setup-apple-certificates
|
||||
@@ -48,18 +60,29 @@ jobs:
|
||||
- name: Build
|
||||
if: ${{ startsWith(matrix.id, 'windows-') }}
|
||||
run: |
|
||||
.\configure ${{ env.FRIDA_CORE_OPTIONS }}
|
||||
.\configure `
|
||||
--host=${{ matrix.id }} `
|
||||
--enable-compiler-backend `
|
||||
${{ env.FRIDA_CORE_OPTIONS }}
|
||||
.\make
|
||||
env:
|
||||
MSYS2_LOCATION: ${{ steps.msys2.outputs.msys2-location }}
|
||||
- name: Build
|
||||
if: ${{ !startsWith(matrix.id, 'windows-') && matrix.id != 'linux-x86' }}
|
||||
run: |
|
||||
./configure ${{ env.FRIDA_CORE_OPTIONS }}
|
||||
./configure \
|
||||
--enable-compiler-backend \
|
||||
${{ env.FRIDA_CORE_OPTIONS }}
|
||||
make
|
||||
- name: Build
|
||||
if: matrix.id == 'linux-x86'
|
||||
run: |
|
||||
CC="gcc -m32" CXX="g++ -m32" STRIP="strip" \
|
||||
./configure --build=linux-x86 --host=linux-x86 ${{ env.FRIDA_CORE_OPTIONS }}
|
||||
./configure \
|
||||
--build=linux-x86 \
|
||||
--host=linux-x86 \
|
||||
--enable-compiler-backend \
|
||||
${{ env.FRIDA_CORE_OPTIONS }}
|
||||
make
|
||||
- name: Upload devkit
|
||||
if: ${{ !startsWith(matrix.id, 'linux-') }}
|
||||
@@ -68,6 +91,7 @@ jobs:
|
||||
name: core-devkit-${{ matrix.id }}
|
||||
path: build/src/devkit/
|
||||
- name: Test
|
||||
if: ${{ matrix.id != 'windows-arm64' && !startsWith(matrix.id, 'macos-') }}
|
||||
run: make test
|
||||
|
||||
cross:
|
||||
@@ -91,9 +115,17 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install ${{ matrix.pkg }}
|
||||
- name: Install Go toolchain
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'src/compiler/go.mod'
|
||||
cache-dependency-path: 'src/compiler/go.mod'
|
||||
- name: Build
|
||||
run: |
|
||||
./configure ${{ matrix.opts }} ${{ env.FRIDA_CORE_OPTIONS }}
|
||||
./configure \
|
||||
${{ matrix.opts }} \
|
||||
${{ !contains(matrix.id, 'mips') && '--enable-compiler-backend' || '' }} \
|
||||
${{ env.FRIDA_CORE_OPTIONS }}
|
||||
make
|
||||
- name: Upload devkit
|
||||
if: ${{ !startsWith(matrix.id, 'linux-') }}
|
||||
@@ -114,9 +146,17 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install Go toolchain
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'src/compiler/go.mod'
|
||||
cache-dependency-path: 'src/compiler/go.mod'
|
||||
- name: Build
|
||||
run: |
|
||||
./configure --host=$XTOOLS_HOST ${{ env.FRIDA_CORE_OPTIONS }}
|
||||
./configure \
|
||||
--host=$XTOOLS_HOST \
|
||||
${{ !startsWith(matrix.arch, 'mips') && '--enable-compiler-backend' || '' }} \
|
||||
${{ env.FRIDA_CORE_OPTIONS }}
|
||||
make
|
||||
- name: Upload devkit
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -127,6 +167,131 @@ jobs:
|
||||
if: matrix.arch == 'x86' || matrix.arch == 'x86_64'
|
||||
run: make test
|
||||
|
||||
emulator:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [armbe8, arm64be]
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/frida/x-tools-linux-${{ matrix.arch }}:latest
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build
|
||||
run: |
|
||||
./configure --host=$XTOOLS_HOST --without-prebuilds=sdk:host --enable-tests
|
||||
make
|
||||
- name: Run custom container
|
||||
uses: addnab/docker-run-action@v3
|
||||
with:
|
||||
image: docker:stable
|
||||
options: -v ${{ github.workspace }}:/workspace:delegated
|
||||
run: |
|
||||
cat <<EOF > /workspace/initscript
|
||||
#!/bin/sh
|
||||
dmesg -n1
|
||||
mount -t proc none /proc
|
||||
mount -t sysfs none /sys
|
||||
mount -t tmpfs none /tmp
|
||||
mount -t devtmpfs none /dev
|
||||
mount -t 9p -o rw,sync,dirsync,relatime,trans=virtio,msize=131072,version=9p2000.L host0 /workspace
|
||||
ip link set lo up
|
||||
ip addr add 192.168.76.20/24 dev eth0
|
||||
ip link set eth0 up
|
||||
ip route add default via 192.168.76.2 dev eth0
|
||||
export LD_LIBRARY_PATH=/lib:/lib64:/libilp32
|
||||
/sbin/entropy
|
||||
cat /etc/motd
|
||||
export PS1='\u@frida:\w\$ '
|
||||
/root/frida-tests
|
||||
status=\$?
|
||||
echo "exit status: \$status"
|
||||
if [ \$status -eq 0 ]; then
|
||||
echo EXIT_OK
|
||||
elif [ \$status -gt 128 ]; then
|
||||
signal=\$((\$status - 128))
|
||||
echo "EXIT_FAIL (terminated by signal \$signal)"
|
||||
else
|
||||
echo "EXIT_FAIL (exit code \$status)"
|
||||
fi
|
||||
EOF
|
||||
|
||||
cat <<EOF > /workspace/launch.sh
|
||||
#!/bin/sh
|
||||
LOGFILE=/workspace/qemu.log
|
||||
: > "\$LOGFILE"
|
||||
timeout 120 qemu-system-aarch64 \
|
||||
-M virt \
|
||||
-machine virtualization=true \
|
||||
-machine type=virt \
|
||||
-cpu cortex-a72 \
|
||||
-smp 4 \
|
||||
-kernel /root/Image.gz \
|
||||
-initrd /root/initramfs.img \
|
||||
-append 'earlyprintk=serial,ttyAMA0 console=ttyAMA0 coredump_filter=0x3f' \
|
||||
-m 4096M \
|
||||
-net nic,id=eth \
|
||||
-net user,id=mynet,net=192.168.76.0/24 \
|
||||
-virtfs local,path=/workspace/,mount_tag=host0,security_model=passthrough,id=host0 \
|
||||
-nographic \
|
||||
-no-reboot \
|
||||
>> "\$LOGFILE" 2>&1 &
|
||||
QEMU_PID=\$!
|
||||
echo "QEMU started with PID \$QEMU_PID"
|
||||
tail -n 0 -f "\$LOGFILE" | while read line; do
|
||||
echo "\$line"
|
||||
echo "\$line" | grep "EXIT_OK" >/dev/null 2>&1
|
||||
if [ \$? -eq 0 ]; then
|
||||
echo "Success signal received from guest. Killing QEMU..."
|
||||
kill -9 "\$QEMU_PID"
|
||||
cat -n "\$LOGFILE"
|
||||
exit 0
|
||||
fi
|
||||
echo "\$line" | grep "EXIT_FAIL" >/dev/null 2>&1
|
||||
if [ \$? -eq 0 ]; then
|
||||
echo "Failure signal received from guest. Killing QEMU..."
|
||||
kill -9 "\$QEMU_PID"
|
||||
cat -n "\$LOGFILE"
|
||||
exit 1
|
||||
fi
|
||||
if ! kill -0 "\$QEMU_PID" 2>/dev/null; then
|
||||
echo "QEMU process has exited."
|
||||
cat -n "\$LOGFILE"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
EOF
|
||||
|
||||
cat <<EOF > /workspace/Dockerfile.ci
|
||||
FROM ghcr.io/frida/x-tools-linux-be-target:latest
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
cpio
|
||||
|
||||
RUN mkdir /tmp/initramfs
|
||||
WORKDIR /tmp/initramfs
|
||||
RUN cpio --extract < /root/initramfs.img
|
||||
|
||||
RUN mkdir /tmp/initramfs/workspace
|
||||
|
||||
COPY initscript /tmp/initramfs/init
|
||||
RUN chmod +x /tmp/initramfs/init
|
||||
|
||||
COPY build/tests /tmp/initramfs/root/
|
||||
RUN find . | cpio -o --format=newc -R root:root > /root/initramfs.img
|
||||
|
||||
COPY launch.sh /root/launch.sh
|
||||
RUN chmod +x /root/launch.sh
|
||||
ENTRYPOINT ["/root/launch.sh"]
|
||||
EOF
|
||||
|
||||
docker build -t ci-image -f /workspace/Dockerfile.ci /workspace
|
||||
docker run --rm -v /workspace:/workspace ci-image
|
||||
|
||||
mobile:
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -143,6 +308,11 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install Go toolchain
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'src/compiler/go.mod'
|
||||
cache-dependency-path: 'src/compiler/go.mod'
|
||||
- name: Set up Apple certificates
|
||||
if: startsWith(matrix.id, 'ios-')
|
||||
uses: ./.github/actions/setup-apple-certificates
|
||||
@@ -160,7 +330,10 @@ jobs:
|
||||
run: echo ANDROID_NDK_ROOT=${{ steps.setup-ndk.outputs.ndk-path }} >> $GITHUB_ENV
|
||||
- name: Build
|
||||
run: |
|
||||
./configure --host=${{ matrix.id }} ${{ env.FRIDA_CORE_OPTIONS }}
|
||||
./configure \
|
||||
--host=${{ matrix.id }} \
|
||||
--enable-compiler-backend \
|
||||
${{ env.FRIDA_CORE_OPTIONS }}
|
||||
make
|
||||
- name: Upload devkit
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -250,6 +423,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86, x86_64, arm, armbe8, arm64, arm64be, arm64beilp32, mips, mipsel, mips64, mips64el]
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/frida/core-linux-helpers-${{ matrix.arch }}:latest
|
||||
steps:
|
||||
@@ -263,7 +437,40 @@ jobs:
|
||||
- name: Check for unexpected changes
|
||||
run: |
|
||||
git config --global --add safe.directory "$(realpath .)"
|
||||
status_output="$(git status --porcelain)"
|
||||
status_output=$(git status --porcelain tests/labrats/{resident-agent,simple-agent}-linux-${{ matrix.arch }}.so tests/labrats/{forker,sleeper,spawner}-linux-${{ matrix.arch }})
|
||||
if [ -n "$status_output" ]; then
|
||||
echo "Unexpected changes detected:"
|
||||
echo "$status_output"
|
||||
echo "Diff:"
|
||||
git diff
|
||||
exit 1
|
||||
else
|
||||
echo "No unexpected changes detected."
|
||||
fi
|
||||
|
||||
# This job is used to check that the Linux labrats are built correctly for all
|
||||
# supported architectures. We do this because the binary artifacts are checked
|
||||
# in, and we want to ensure that they are up-to-date with respect to the source
|
||||
# for all architectures.
|
||||
check-labrats:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86, x86_64, arm, armbe8, arm64, arm64be, arm64beilp32, mips, mipsel, mips64, mips64el]
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/frida/core-linux-helpers-${{ matrix.arch }}:latest
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build
|
||||
run: |
|
||||
./tests/labrats/rebuild.sh ${{ matrix.arch }}
|
||||
- name: Check for unexpected changes
|
||||
run: |
|
||||
git config --global --add safe.directory "$(realpath .)"
|
||||
status_output=$(git status --porcelain src/linux/helpers/{bootstrapper,loader}-${{ matrix.arch }}.bin)
|
||||
if [ -n "$status_output" ]; then
|
||||
echo "Unexpected changes detected:"
|
||||
echo "$status_output"
|
||||
|
||||
@@ -3,9 +3,16 @@
|
||||
/src/barebone/helpers/memory-scanner/target/
|
||||
/src/barebone/helpers/target/
|
||||
/src/barebone/node_modules/
|
||||
/src/barebone/script-runtime.js
|
||||
/src/compiler/node_modules/
|
||||
/src/darwin/agent/osanalytics/node_modules/
|
||||
/src/darwin/agent/osanalytics/osanalytics.js
|
||||
/src/darwin/agent/reportcrash/node_modules/
|
||||
/src/darwin/agent/reportcrash/reportcrash.js
|
||||
/src/droidy/helper/build/
|
||||
/src/fruity/helpers/build/
|
||||
/src/linux/agent/system-server/node_modules/
|
||||
/src/linux/agent/system-server/system-server.js
|
||||
/src/linux/helpers/build/
|
||||
/src/linux/helpers/ext/
|
||||
/subprojects/*
|
||||
@@ -435,6 +435,7 @@ def compile(privdir: Path, state: State):
|
||||
environ={**build_env, **group.extra_environ},
|
||||
allowed_prebuilds=state.allowed_prebuilds,
|
||||
extra_meson_options=[
|
||||
"-Dcompiler_backend=disabled",
|
||||
"-Dhelper_modern=",
|
||||
"-Dhelper_legacy=",
|
||||
"-Dagent_modern=",
|
||||
@@ -499,6 +500,8 @@ def option_should_be_forwarded(k: "OptionKey",
|
||||
|
||||
if k.is_project():
|
||||
if is_for_us:
|
||||
if k.name == "compiler_backend":
|
||||
return False
|
||||
tokens = k.name.split("_")
|
||||
if tokens[0] in {"helper", "agent"} and tokens[-1] in {"modern", "legacy"}:
|
||||
return False
|
||||
|
||||
+14
-1
@@ -446,7 +446,14 @@ namespace Frida {
|
||||
|
||||
public string read_fixed_string (size_t offset, size_t size) throws Error {
|
||||
string * start = (string *) get_pointer (offset, size);
|
||||
string val = start->substring (0, (long) size);
|
||||
size_t max_length = size - offset;
|
||||
string * end = memchr (start, 0, max_length);
|
||||
size_t n;
|
||||
if (end != null)
|
||||
n = end - start;
|
||||
else
|
||||
n = size;
|
||||
string val = start->substring (0, (long) n);
|
||||
if (!val.validate ())
|
||||
throw new Error.PROTOCOL ("Invalid UTF-8 string");
|
||||
return val;
|
||||
@@ -579,6 +586,12 @@ namespace Frida {
|
||||
return val;
|
||||
}
|
||||
|
||||
public unowned BufferReader skip (size_t n) throws Error {
|
||||
check_available (n);
|
||||
offset += n;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void check_available (size_t n) throws Error {
|
||||
if (available < n)
|
||||
throw new Error.PROTOCOL ("Malformed buffer: truncated");
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
namespace Frida.FS {
|
||||
public void mkdirp (File dir, Cancellable? cancellable = null) throws Error {
|
||||
try {
|
||||
dir.make_directory_with_parents (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (!(e is IOError.EXISTS))
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public void rmtree (File dir, Cancellable? cancellable = null) throws Error {
|
||||
rmtree_worker (dir, 0, cancellable);
|
||||
}
|
||||
|
||||
private void rmtree_worker (File dir, uint depth, Cancellable? cancellable = null) throws Error {
|
||||
FileEnumerator enumerator;
|
||||
try {
|
||||
enumerator = dir.enumerate_children (FileAttribute.STANDARD_NAME, NOFOLLOW_SYMLINKS, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (depth == 0 && e is IOError.NOT_FOUND)
|
||||
return;
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
|
||||
try {
|
||||
FileInfo? info;
|
||||
File? child;
|
||||
while (enumerator.iterate (out info, out child, cancellable) && info != null) {
|
||||
if (info == null)
|
||||
continue;
|
||||
if (info.get_file_type () == DIRECTORY)
|
||||
rmtree (child, cancellable);
|
||||
else
|
||||
child.delete (cancellable);
|
||||
}
|
||||
|
||||
dir.delete (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (!(e is IOError.EXISTS))
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async void rmtree_async (File dir, Cancellable? cancellable = null) throws Error, IOError {
|
||||
yield rmtree_async_worker (dir, 0, cancellable);
|
||||
}
|
||||
|
||||
private async void rmtree_async_worker (File dir, uint depth, Cancellable? cancellable = null) throws Error, IOError {
|
||||
int io_priority = Priority.DEFAULT;
|
||||
|
||||
FileEnumerator enumerator;
|
||||
try {
|
||||
enumerator = yield dir.enumerate_children_async (FileAttribute.STANDARD_NAME, NOFOLLOW_SYMLINKS, io_priority,
|
||||
cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (depth == 0 && e is IOError.NOT_FOUND)
|
||||
return;
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
var batch = yield enumerator.next_files_async (1000, io_priority, cancellable);
|
||||
if (batch.is_empty ())
|
||||
break;
|
||||
foreach (FileInfo info in batch) {
|
||||
File child = dir.get_child (info.get_name ());
|
||||
if (info.get_file_type () == DIRECTORY)
|
||||
yield rmtree_async_worker (child, depth + 1, cancellable);
|
||||
else
|
||||
yield child.delete_async (io_priority, cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
yield dir.delete_async (io_priority, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async string read_all_text (File f, Cancellable? cancellable) throws Error, IOError {
|
||||
Bytes b = yield read_all_bytes (f, cancellable);
|
||||
string text = (string) b.get_data ();
|
||||
if (!text.validate ())
|
||||
throw new Error.PROTOCOL ("Unable to read '%s': invalid UTF-8", f.get_parse_name ());
|
||||
return text;
|
||||
}
|
||||
|
||||
public async void write_all_text (File f, string t, Cancellable? cancellable) throws Error, IOError {
|
||||
yield write_all_bytes (f, new Bytes (t.data), cancellable);
|
||||
}
|
||||
|
||||
public async Bytes read_all_bytes (File f, Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
return yield f.load_bytes_async (cancellable, null);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CANCELLED)
|
||||
throw (IOError) e;
|
||||
if (e is IOError.NOT_FOUND)
|
||||
throw new Error.INVALID_ARGUMENT ("%s", e.message);
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async void write_all_bytes (File f, Bytes b, Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
yield f.replace_contents_bytes_async (b, null, false, FileCreateFlags.NONE, cancellable, null);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CANCELLED)
|
||||
throw (IOError) e;
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef __FRIDA_SYSCALL_H__
|
||||
#define __FRIDA_SYSCALL_H__
|
||||
#ifndef __FRIDA_LINUX_SYSCALL_H__
|
||||
#define __FRIDA_LINUX_SYSCALL_H__
|
||||
|
||||
#include <linux/unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
@@ -14,7 +14,7 @@ namespace Frida {
|
||||
[CCode (cheader_filename = "sys/mman.h", cname = "MAP_ANONYMOUS")]
|
||||
public const int MAP_ANONYMOUS;
|
||||
|
||||
[CCode (cheader_filename = "linux/frida-syscall.h", cprefix = "SYS_", has_type_id = false)]
|
||||
[CCode (cheader_filename = "linux-syscall.h", cprefix = "SYS_", has_type_id = false)]
|
||||
public enum SysCall {
|
||||
memfd_create,
|
||||
pidfd_getfd,
|
||||
@@ -0,0 +1,107 @@
|
||||
namespace Frida {
|
||||
private uint linux_major = 0;
|
||||
private uint linux_minor = 0;
|
||||
|
||||
public bool check_kernel_version (uint major, uint minor) {
|
||||
if (linux_major == 0) {
|
||||
var name = Posix.utsname ();
|
||||
name.release.scanf ("%u.%u", out linux_major, out linux_minor);
|
||||
}
|
||||
|
||||
return (linux_major == major && linux_minor >= minor) || linux_major > major;
|
||||
}
|
||||
|
||||
public class FileDescriptor : Object, FileDescriptorBased {
|
||||
public int handle;
|
||||
|
||||
public FileDescriptor (int handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
~FileDescriptor () {
|
||||
if (handle != -1)
|
||||
Posix.close (handle);
|
||||
}
|
||||
|
||||
public int steal () {
|
||||
int result = handle;
|
||||
handle = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
public int get_fd () {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PidFileDescriptor : FileDescriptor {
|
||||
private uint pid;
|
||||
|
||||
private PidFileDescriptor (int fd, uint pid) {
|
||||
base (fd);
|
||||
this.pid = pid;
|
||||
}
|
||||
|
||||
public static bool is_supported () {
|
||||
return check_kernel_version (5, 3);
|
||||
}
|
||||
|
||||
public static bool getfd_is_supported () {
|
||||
return check_kernel_version (5, 6);
|
||||
}
|
||||
|
||||
public static PidFileDescriptor from_pid (uint pid) throws Error {
|
||||
int fd = pidfd_open (pid, 0);
|
||||
if (fd == -1)
|
||||
throw_pidfd_error (pid, errno);
|
||||
return new PidFileDescriptor (fd, pid);
|
||||
}
|
||||
|
||||
public FileDescriptor getfd (int targetfd) throws Error {
|
||||
int fd = pidfd_getfd (handle, targetfd, 0);
|
||||
if (fd == -1)
|
||||
throw_pidfd_error (pid, errno);
|
||||
return new FileDescriptor (fd);
|
||||
}
|
||||
|
||||
private static int pidfd_open (uint pid, uint flags) {
|
||||
return Linux.syscall (SysCall.pidfd_open, pid, flags);
|
||||
}
|
||||
|
||||
private static int pidfd_getfd (int pidfd, int targetfd, uint flags) {
|
||||
return Linux.syscall (SysCall.pidfd_getfd, pidfd, targetfd, flags);
|
||||
}
|
||||
|
||||
[NoReturn]
|
||||
private static void throw_pidfd_error (uint pid, int err) throws Error {
|
||||
switch (err) {
|
||||
case Posix.ESRCH:
|
||||
throw new Error.PROCESS_NOT_FOUND ("Process not found");
|
||||
case Posix.EPERM:
|
||||
throw new Error.PERMISSION_DENIED ("Unable to use pidfd for pid %u: %s", pid, strerror (err));
|
||||
default:
|
||||
throw new Error.NOT_SUPPORTED ("Unable to use pidfd for pid %u: %s", pid, strerror (err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace MemoryFileDescriptor {
|
||||
public bool is_supported () {
|
||||
return check_kernel_version (3, 17);
|
||||
}
|
||||
|
||||
public static FileDescriptor from_bytes (string name, Bytes bytes) {
|
||||
assert (is_supported ());
|
||||
|
||||
var fd = new FileDescriptor (memfd_create (name, 0));
|
||||
unowned uint8[] data = bytes.get_data ();
|
||||
ssize_t n = Posix.write (fd.handle, data, data.length);
|
||||
assert (n == data.length);
|
||||
return fd;
|
||||
}
|
||||
|
||||
private int memfd_create (string name, uint flags) {
|
||||
return Linux.syscall (SysCall.memfd_create, name, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
-1
@@ -9,6 +9,7 @@ base_sources = [
|
||||
'p2p-glue.c',
|
||||
'dbus.vala',
|
||||
'promise.vala',
|
||||
'fs.vala',
|
||||
'socket.vala',
|
||||
'socket-glue.c',
|
||||
'buffer.vala',
|
||||
@@ -28,6 +29,15 @@ if host_os_family == 'darwin'
|
||||
base_sources += 'corefoundation.vala'
|
||||
endif
|
||||
|
||||
if host_os_family == 'linux'
|
||||
base_sources += 'linux.vala'
|
||||
extra_vala_args += [
|
||||
'--pkg=linux',
|
||||
'--pkg=linux-system',
|
||||
'--pkg=posix',
|
||||
]
|
||||
endif
|
||||
|
||||
extra_deps = []
|
||||
if host_os_family != 'windows'
|
||||
extra_vala_args += '--pkg=gio-unix-2.0'
|
||||
@@ -45,6 +55,12 @@ if host_os_family == 'darwin'
|
||||
'--pkg=darwin-xpc',
|
||||
]
|
||||
endif
|
||||
if host_os_family == 'linux'
|
||||
base_vala_args += [
|
||||
'--vapidir=' + meson.current_source_dir(),
|
||||
'--pkg=gio-unix-2.0',
|
||||
]
|
||||
endif
|
||||
|
||||
base_gir_name = f'FridaBase-@[email protected]'
|
||||
|
||||
@@ -62,7 +78,7 @@ base = static_library('frida-base-' + api_version, base_sources,
|
||||
base_dep = declare_dependency(
|
||||
include_directories: include_directories('.'),
|
||||
link_with: base,
|
||||
dependencies: [nice_dep, json_glib_dep, gum_dep],
|
||||
dependencies: [nice_dep, json_glib_dep, gum_dep] + extra_deps,
|
||||
)
|
||||
|
||||
base_build_dir = meson.current_build_dir()
|
||||
|
||||
+11
-11
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
[DBus (name = "re.frida.HostSession16")]
|
||||
[DBus (name = "re.frida.HostSession17")]
|
||||
public interface HostSession : Object {
|
||||
public abstract async void ping (uint interval_seconds, Cancellable? cancellable) throws GLib.Error;
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Frida {
|
||||
public signal void uninjected (InjectorPayloadId id);
|
||||
}
|
||||
|
||||
[DBus (name = "re.frida.AgentSessionProvider16")]
|
||||
[DBus (name = "re.frida.AgentSessionProvider17")]
|
||||
public interface AgentSessionProvider : Object {
|
||||
public abstract async void open (AgentSessionId id, HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws GLib.Error;
|
||||
@@ -58,7 +58,7 @@ namespace Frida {
|
||||
public signal void child_gating_changed (uint subscriber_count);
|
||||
}
|
||||
|
||||
[DBus (name = "re.frida.AgentSession16")]
|
||||
[DBus (name = "re.frida.AgentSession17")]
|
||||
public interface AgentSession : Object {
|
||||
public abstract async void close (Cancellable? cancellable) throws GLib.Error;
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace Frida {
|
||||
public signal void candidate_gathering_done ();
|
||||
}
|
||||
|
||||
[DBus (name = "re.frida.AgentController16")]
|
||||
[DBus (name = "re.frida.AgentController17")]
|
||||
public interface AgentController : Object {
|
||||
#if !WINDOWS
|
||||
public abstract async HostChildId prepare_to_fork (uint parent_pid, Cancellable? cancellable, out uint parent_injectee_id,
|
||||
@@ -121,7 +121,7 @@ namespace Frida {
|
||||
Cancellable? cancellable) throws GLib.Error;
|
||||
}
|
||||
|
||||
[DBus (name = "re.frida.AgentMessageSink16")]
|
||||
[DBus (name = "re.frida.AgentMessageSink17")]
|
||||
public interface AgentMessageSink : Object {
|
||||
public abstract async void post_messages (AgentMessage[] messages, uint batch_id,
|
||||
Cancellable? cancellable) throws GLib.Error;
|
||||
@@ -749,14 +749,14 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
[DBus (name = "re.frida.Channel16")]
|
||||
[DBus (name = "re.frida.Channel17")]
|
||||
public interface Channel : Object {
|
||||
public abstract async void close (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void input (uint8[] data, Cancellable? cancellable) throws GLib.Error;
|
||||
public signal void output (uint8[] data);
|
||||
}
|
||||
|
||||
[DBus (name = "re.frida.ServiceSession16")]
|
||||
[DBus (name = "re.frida.ServiceSession17")]
|
||||
public interface ServiceSession : Object {
|
||||
public signal void close ();
|
||||
public signal void message (Variant message);
|
||||
@@ -766,13 +766,13 @@ namespace Frida {
|
||||
public abstract async Variant request (Variant parameters, Cancellable? cancellable) throws GLib.Error;
|
||||
}
|
||||
|
||||
[DBus (name = "re.frida.TransportBroker16")]
|
||||
[DBus (name = "re.frida.TransportBroker17")]
|
||||
public interface TransportBroker : Object {
|
||||
public abstract async void open_tcp_transport (AgentSessionId id, Cancellable? cancellable, out uint16 port,
|
||||
out string token) throws GLib.Error;
|
||||
}
|
||||
|
||||
[DBus (name = "re.frida.PortalSession16")]
|
||||
[DBus (name = "re.frida.PortalSession17")]
|
||||
public interface PortalSession : Object {
|
||||
public abstract async void join (HostApplicationInfo app, SpawnStartState current_state,
|
||||
AgentSessionId[] interrupted_sessions, HashTable<string, Variant> options, Cancellable? cancellable,
|
||||
@@ -781,14 +781,14 @@ namespace Frida {
|
||||
public signal void kill ();
|
||||
}
|
||||
|
||||
[DBus (name = "re.frida.BusSession16")]
|
||||
[DBus (name = "re.frida.BusSession17")]
|
||||
public interface BusSession : Object {
|
||||
public abstract async void attach (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void post (string json, bool has_data, uint8[] data, Cancellable? cancellable) throws GLib.Error;
|
||||
public signal void message (string json, bool has_data, uint8[] data);
|
||||
}
|
||||
|
||||
[DBus (name = "re.frida.AuthenticationService16")]
|
||||
[DBus (name = "re.frida.AuthenticationService17")]
|
||||
public interface AuthenticationService : Object {
|
||||
public abstract async string authenticate (string token, Cancellable? cancellable) throws GLib.Error;
|
||||
}
|
||||
|
||||
@@ -548,12 +548,17 @@ namespace Frida {
|
||||
|
||||
IOStream soup_stream = connection.get_io_stream ();
|
||||
|
||||
SocketConnection socket_stream;
|
||||
soup_stream.get ("base-iostream", out socket_stream);
|
||||
IOStream base_iostream;
|
||||
soup_stream.get ("base-iostream", out base_iostream);
|
||||
|
||||
SocketAddress remote_address;
|
||||
SocketAddress? remote_address = null;
|
||||
SocketConnection? sc = base_iostream as SocketConnection;
|
||||
if (sc == null) {
|
||||
IOStream tls_stream = base_iostream;
|
||||
tls_stream.get ("base-io-stream", out sc);
|
||||
}
|
||||
try {
|
||||
remote_address = socket_stream.get_remote_address ();
|
||||
remote_address = sc.get_remote_address ();
|
||||
} catch (GLib.Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
@@ -136,6 +136,8 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public delegate int TranslateErrorFunc (string domain, int code, string description);
|
||||
|
||||
private Gee.Deque<Scope> scopes = new Gee.ArrayQueue<Scope> ();
|
||||
|
||||
public XpcObjectReader (Darwin.Xpc.Object obj) {
|
||||
@@ -224,6 +226,28 @@ namespace Frida {
|
||||
return error.get_string (Darwin.Xpc.Error.KEY_DESCRIPTION);
|
||||
}
|
||||
|
||||
public void check_nserror (TranslateErrorFunc translate_error) throws Error {
|
||||
if (!has_member ("error"))
|
||||
return;
|
||||
|
||||
read_member ("error");
|
||||
|
||||
unowned string domain = read_member ("domain").get_string_value ();
|
||||
end_member ();
|
||||
|
||||
var code = (int) read_member ("code").get_int64_value ();
|
||||
end_member ();
|
||||
|
||||
string description = read_member ("userInfo").read_member ("NSLocalizedDescription").get_string_value ();
|
||||
if (description.has_suffix ("."))
|
||||
description = description[:description.length - 1];
|
||||
|
||||
throw (Error) new GLib.Error.literal (
|
||||
Quark.from_string ("frida-error-quark"),
|
||||
translate_error (domain, code, description),
|
||||
description);
|
||||
}
|
||||
|
||||
public unowned Darwin.Xpc.Object get_object_value (Darwin.Xpc.Type expected_type) throws Error {
|
||||
return peek_scope ().get_object<Darwin.Xpc.Object> (expected_type);
|
||||
}
|
||||
|
||||
+10
-4
@@ -1242,7 +1242,13 @@ namespace Frida.Gadget {
|
||||
}
|
||||
|
||||
private sealed class Script : Object, RpcPeer {
|
||||
private const uint8 QUICKJS_BYTECODE_MAGIC = 0x02;
|
||||
private const uint8 QUICKJS_BYTECODE_MAGIC =
|
||||
#if BIG_ENDIAN
|
||||
0x42
|
||||
#else
|
||||
0x02
|
||||
#endif
|
||||
;
|
||||
|
||||
public enum ChangeBehavior {
|
||||
IGNORE,
|
||||
@@ -1694,7 +1700,7 @@ namespace Frida.Gadget {
|
||||
public abstract async void close (Cancellable? cancellable = null) throws IOError;
|
||||
}
|
||||
|
||||
private sealed class AuthenticationChannel : Object, Peer, AuthenticationService {
|
||||
private class AuthenticationChannel : Object, Peer, AuthenticationService {
|
||||
public weak ControlServer parent {
|
||||
get;
|
||||
construct;
|
||||
@@ -1742,7 +1748,7 @@ namespace Frida.Gadget {
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ControlChannel : Object, Peer, HostSession {
|
||||
private class ControlChannel : Object, Peer, HostSession {
|
||||
public weak ControlServer parent {
|
||||
get;
|
||||
construct;
|
||||
@@ -1941,7 +1947,7 @@ namespace Frida.Gadget {
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class LiveAgentSession : BaseAgentSession {
|
||||
private class LiveAgentSession : BaseAgentSession {
|
||||
public ControlChannel? controller {
|
||||
get;
|
||||
set;
|
||||
|
||||
@@ -55,6 +55,7 @@ struct _FridaUnwindHookState
|
||||
gpointer set_info_original;
|
||||
void (* set_info) (gpointer cursor, gint is_return_address);
|
||||
gpointer (* get_reg) (gpointer cursor, gint reg);
|
||||
GumInterceptor * interceptor;
|
||||
};
|
||||
|
||||
#if __has_feature (ptrauth_calls)
|
||||
@@ -97,7 +98,6 @@ _frida_unwind_sitter_hook_libunwind (void)
|
||||
#if GLIB_SIZEOF_VOID_P == 8
|
||||
gpointer * set_info_slot;
|
||||
gpointer get_reg_impl;
|
||||
GumInterceptor * interceptor;
|
||||
|
||||
if (state != NULL)
|
||||
return;
|
||||
@@ -121,8 +121,9 @@ _frida_unwind_sitter_hook_libunwind (void)
|
||||
state->set_info = FRIDA_RESIGN_PTR (state->set_info_original);
|
||||
state->get_reg = FRIDA_RESIGN_PTR (get_reg_impl);
|
||||
|
||||
interceptor = gum_interceptor_obtain ();
|
||||
if (gum_interceptor_replace (interceptor, state->set_info_original, frida_unwind_cursor_set_info_replacement, NULL, NULL)
|
||||
state->interceptor = gum_interceptor_obtain ();
|
||||
|
||||
if (gum_interceptor_replace (state->interceptor, state->set_info_original, frida_unwind_cursor_set_info_replacement, NULL, NULL)
|
||||
!= GUM_REPLACE_OK)
|
||||
goto unsupported_version;
|
||||
|
||||
@@ -137,13 +138,12 @@ unsupported_version:
|
||||
void
|
||||
_frida_unwind_sitter_unhook_libunwind (void)
|
||||
{
|
||||
GumInterceptor * interceptor;
|
||||
|
||||
if (state == NULL)
|
||||
return;
|
||||
|
||||
interceptor = gum_interceptor_obtain ();
|
||||
gum_interceptor_revert (interceptor, state->set_info_original);
|
||||
gum_interceptor_revert (state->interceptor, state->set_info_original);
|
||||
|
||||
g_object_unref (state->interceptor);
|
||||
|
||||
g_slice_free (FridaUnwindHookState, state);
|
||||
state = NULL;
|
||||
|
||||
+105
-63
@@ -6,6 +6,8 @@ project('frida-core', 'vala', 'c', 'cpp',
|
||||
default_options: ['c_std=gnu99,c99', 'cpp_std=c++17'],
|
||||
)
|
||||
|
||||
fs = import('fs')
|
||||
|
||||
if not meson.get_compiler('vala').version().endswith('-frida')
|
||||
error('''Incompatible Vala compiler detected.
|
||||
|
||||
@@ -20,7 +22,7 @@ cc = meson.get_compiler('c')
|
||||
cpp = meson.get_compiler('cpp')
|
||||
|
||||
releng = meson.global_source_root() / 'releng'
|
||||
if not import('fs').exists(releng)
|
||||
if not fs.exists(releng)
|
||||
releng = meson.project_source_root() / 'releng'
|
||||
endif
|
||||
|
||||
@@ -98,11 +100,6 @@ else
|
||||
host_cpu_mode = '32'
|
||||
endif
|
||||
host_docks_mobile_devices = host_os in ['windows', 'macos', 'linux'] and host_cpu_mode == '64'
|
||||
gumjs_archs = {
|
||||
'x86': 'ia32',
|
||||
'x86_64': 'x64',
|
||||
}
|
||||
host_arch_gumjs = gumjs_archs.get(host_arch, host_arch)
|
||||
|
||||
if host_os_family == 'darwin'
|
||||
c_languages += ['objc', 'objcpp']
|
||||
@@ -162,6 +159,14 @@ if host_os_family == 'darwin'
|
||||
endif
|
||||
endif
|
||||
|
||||
if host_machine.endian() == 'big'
|
||||
if host_abi in ['arm', 'armhf']
|
||||
host_abi = 'armbe8'
|
||||
elif host_abi == 'arm64'
|
||||
host_abi = 'arm64be'
|
||||
endif
|
||||
endif
|
||||
|
||||
if host_os_family == 'darwin' and host_arch == 'arm64'
|
||||
host_is_modern = host_abi == 'arm64e'
|
||||
else
|
||||
@@ -354,6 +359,44 @@ if host_os == 'android'
|
||||
])
|
||||
endif
|
||||
|
||||
if host_toolchain == 'microsoft'
|
||||
lib = find_program('lib')
|
||||
ar = ''
|
||||
nm = ''
|
||||
ranlib = ''
|
||||
strip = ''
|
||||
else
|
||||
lib = ''
|
||||
ar = find_program('ar')
|
||||
nm = find_program('nm')
|
||||
ranlib = find_program('ranlib')
|
||||
strip = find_program('strip')
|
||||
endif
|
||||
if host_os_family != 'windows'
|
||||
if host_os_family == 'darwin'
|
||||
readelf = ''
|
||||
otool = find_program('otool')
|
||||
libtool = find_program('libtool')
|
||||
else
|
||||
readelf = find_program('readelf')
|
||||
otool = ''
|
||||
libtool = ''
|
||||
endif
|
||||
else
|
||||
readelf = ''
|
||||
otool = ''
|
||||
libtool = ''
|
||||
endif
|
||||
if host_os_family == 'darwin'
|
||||
install_name_tool = find_program('install_name_tool')
|
||||
lipo = find_program('lipo')
|
||||
codesign = find_program('codesign')
|
||||
else
|
||||
install_name_tool = ''
|
||||
lipo = ''
|
||||
codesign = ''
|
||||
endif
|
||||
|
||||
have_local_backend = get_option('local_backend').allowed()
|
||||
have_fruity_backend = get_option('fruity_backend') \
|
||||
.disable_auto_if(not host_docks_mobile_devices) \
|
||||
@@ -366,15 +409,54 @@ have_barebone_backend = get_option('barebone_backend') \
|
||||
.disable_if(not quickjs_dep.found()) \
|
||||
.disable_auto_if(not host_docks_mobile_devices) \
|
||||
.allowed()
|
||||
have_compiler_backend = get_option('compiler_backend') \
|
||||
.disable_if(not have_local_backend, error_message: 'compiler backend requires the local backend for script execution') \
|
||||
.disable_auto_if(host_os == 'watchos') \
|
||||
.allowed()
|
||||
if get_option('compiler_snapshot').auto()
|
||||
# Generating our snapshot with V8's CPU simulator takes a very long time.
|
||||
generate_compiler_snapshot = meson.can_run_host_binaries()
|
||||
else
|
||||
generate_compiler_snapshot = get_option('compiler_snapshot').enabled()
|
||||
compiler_backend_opt = get_option('compiler_backend') \
|
||||
.disable_auto_if(host_os == 'watchos')
|
||||
have_compiler_backend = false
|
||||
if compiler_backend_opt.allowed()
|
||||
go = find_program('go', native: true, required: false)
|
||||
if go.found()
|
||||
message('Detecting cgo availability')
|
||||
detect_output = run_command(
|
||||
[
|
||||
python,
|
||||
files('tools' / 'cgo' / 'detect.py'),
|
||||
go,
|
||||
host_os,
|
||||
host_abi,
|
||||
shlib_suffix,
|
||||
cc.get_id(),
|
||||
'>>>', cc.cmd_array(), '<<<',
|
||||
'>>>', ar, '<<<',
|
||||
'>>>', nm, '<<<',
|
||||
'>>>', ranlib, '<<<',
|
||||
],
|
||||
capture: true,
|
||||
check: true
|
||||
).stdout()
|
||||
lines = detect_output.split('\n')
|
||||
status = lines[0]
|
||||
if status == 'ok'
|
||||
have_compiler_backend = true
|
||||
tokens = lines[1].split(' ')
|
||||
go_mode = tokens[0]
|
||||
go_config = tokens[1]
|
||||
compiler_backend_static_compilation = go_mode == 'c-archive'
|
||||
if compiler_backend_static_compilation
|
||||
vala_flags += '--define=COMPILER_BACKEND_STATIC_COMPILATION'
|
||||
endif
|
||||
else
|
||||
error_message = lines[1]
|
||||
if compiler_backend_opt.enabled()
|
||||
error(error_message)
|
||||
else
|
||||
warning(f'Building without Compiler backend: @error_message@')
|
||||
endif
|
||||
endif
|
||||
elif compiler_backend_opt.enabled()
|
||||
error('Need Go >= 1.24 to build the Compiler backend powered by ESBuild and typescript-go')
|
||||
else
|
||||
warning('Building without Compiler backend: Go >= 1.24 not found')
|
||||
endif
|
||||
endif
|
||||
|
||||
build_gadget = get_option('gadget') \
|
||||
@@ -414,7 +496,7 @@ foreach b : [['local', have_local_backend],
|
||||
endif
|
||||
endforeach
|
||||
|
||||
if have_barebone_backend or have_compiler_backend
|
||||
if host_os_family == 'darwin' or host_os == 'android' or have_barebone_backend or have_compiler_backend
|
||||
node = find_program('node', version: '>=18.0.0', native: true, required: false)
|
||||
if not node.found()
|
||||
error('Need Node.js >= 18.0.0 to process JavaScript code at build time')
|
||||
@@ -444,12 +526,6 @@ 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']
|
||||
v8_mksnapshot = find_program('v8-mksnapshot-@0@-@1@'.format(host_os, host_abi), required: false)
|
||||
if not v8_mksnapshot.found()
|
||||
v8_mksnapshot = ''
|
||||
endif
|
||||
else
|
||||
v8_mksnapshot = ''
|
||||
endif
|
||||
|
||||
backend_deps_private = []
|
||||
@@ -571,42 +647,6 @@ if host_os in ['ios', 'tvos']
|
||||
backend_libs_private += ['-Wl,-framework,CoreGraphics', '-Wl,-framework,UIKit']
|
||||
endif
|
||||
|
||||
if host_toolchain == 'microsoft'
|
||||
lib = find_program('lib')
|
||||
ar = ''
|
||||
nm = ''
|
||||
strip = ''
|
||||
else
|
||||
lib = ''
|
||||
ar = find_program('ar')
|
||||
nm = find_program('nm')
|
||||
strip = find_program('strip')
|
||||
endif
|
||||
if host_os_family != 'windows'
|
||||
if host_os_family == 'darwin'
|
||||
readelf = ''
|
||||
otool = find_program('otool')
|
||||
libtool = find_program('libtool')
|
||||
else
|
||||
readelf = find_program('readelf')
|
||||
otool = ''
|
||||
libtool = ''
|
||||
endif
|
||||
else
|
||||
readelf = ''
|
||||
otool = ''
|
||||
libtool = ''
|
||||
endif
|
||||
if host_os_family == 'darwin'
|
||||
install_name_tool = find_program('install_name_tool')
|
||||
lipo = find_program('lipo')
|
||||
codesign = find_program('codesign')
|
||||
else
|
||||
install_name_tool = ''
|
||||
lipo = ''
|
||||
codesign = ''
|
||||
endif
|
||||
|
||||
if get_option('devkits').length() != 0
|
||||
if get_option('default_library') != 'static'
|
||||
error('Devkits can only be generated from static libraries')
|
||||
@@ -664,6 +704,8 @@ post_process = [
|
||||
'@INPUT0@',
|
||||
]
|
||||
|
||||
compile_js_agent = files('tools' / 'compile-js-agent.py')
|
||||
|
||||
mapper_opt = get_option('mapper')
|
||||
if mapper_opt.auto()
|
||||
have_mapper = host_os_family == 'darwin'
|
||||
@@ -698,14 +740,15 @@ vala_flags += [
|
||||
'--pkg', 'config',
|
||||
]
|
||||
|
||||
vala_flags += ['--define=' + host_os_family.to_upper()]
|
||||
vala_flags += '--define=' + host_os_family.to_upper()
|
||||
if host_os != host_os_family
|
||||
vala_flags += ['--define=' + host_os.to_upper()]
|
||||
vala_flags += '--define=' + host_os.to_upper()
|
||||
endif
|
||||
vala_flags += ['--define=' + host_arch.to_upper()]
|
||||
vala_flags += '--define=' + host_arch.to_upper()
|
||||
if host_abi != host_arch
|
||||
vala_flags += ['--define=' + host_abi.to_upper()]
|
||||
vala_flags += '--define=' + host_abi.to_upper()
|
||||
endif
|
||||
vala_flags += '--define=' + host_machine.endian().to_upper() + '_ENDIAN'
|
||||
add_project_arguments(vala_flags, language: 'vala')
|
||||
add_project_arguments('-I' + vapidir, language: 'c')
|
||||
|
||||
@@ -750,7 +793,6 @@ summary(
|
||||
'assets': get_option('assets'),
|
||||
'mapper': have_mapper,
|
||||
'connectivity': tls_provider_dep.found(),
|
||||
'compiler_snapshot': generate_compiler_snapshot,
|
||||
'gadget': build_gadget,
|
||||
'server': build_server,
|
||||
'portal': build_portal,
|
||||
|
||||
+1
-7
@@ -37,13 +37,7 @@ option('barebone_backend',
|
||||
option('compiler_backend',
|
||||
type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Include frida-compile backend powered by GumJS',
|
||||
)
|
||||
|
||||
option('compiler_snapshot',
|
||||
type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Speed up compiler startup by using a snapshot'
|
||||
description: 'Include frida-compile backend powered by ESBuild and typescript-go',
|
||||
)
|
||||
|
||||
option('assets',
|
||||
|
||||
+1
-1
Submodule releng updated: 2eecc41cbe...e10baa39a2.
+91284
File diff suppressed because it is too large.
Load diff
+29500
File diff suppressed because it is too large.
Load diff
+5092
-4567
File diff suppressed because it is too large.
Load diff
+46
-7
@@ -16,6 +16,7 @@ GIR_NAMESPACES = {
|
||||
"glib": GLIB_NAMESPACE,
|
||||
}
|
||||
|
||||
CORE_TAG_IMPLEMENTS = f"{{{CORE_NAMESPACE}}}implements"
|
||||
CORE_TAG_FIELD = f"{{{CORE_NAMESPACE}}}field"
|
||||
CORE_TAG_CONSTRUCTOR = f"{{{CORE_NAMESPACE}}}constructor"
|
||||
CORE_TAG_METHOD = f"{{{CORE_NAMESPACE}}}method"
|
||||
@@ -25,6 +26,11 @@ OBJECT_TYPE_PATTERN = re.compile(r"\bpublic\s+(sealed )?(class|interface)\s+(\w+
|
||||
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'))
|
||||
@@ -37,6 +43,13 @@ def main():
|
||||
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()
|
||||
@@ -52,6 +65,7 @@ def main():
|
||||
|
||||
toplevel_names = [
|
||||
"frida.vala",
|
||||
"package-manager.vala",
|
||||
"control-service.vala",
|
||||
"portal-service.vala",
|
||||
"file-monitor.vala",
|
||||
@@ -77,7 +91,7 @@ def main():
|
||||
elif output_type == 'vapi':
|
||||
enable_vapi = True
|
||||
|
||||
api = parse_api(api_version, toplevel_code, core_header, core_vapi, base_header, base_vapi)
|
||||
api = parse_api(frida_version, frida_version_components, api_version, toplevel_code, core_header, core_vapi, base_header, base_vapi)
|
||||
|
||||
if enable_header:
|
||||
emit_header(api, output_dir)
|
||||
@@ -92,7 +106,26 @@ def emit_header(api, output_dir):
|
||||
with OutputFile(output_dir / 'frida-core.h') as output_header_file:
|
||||
output_header_file.write("#ifndef __FRIDA_CORE_H__\n#define __FRIDA_CORE_H__\n\n")
|
||||
|
||||
output_header_file.write("#include <glib.h>\n#include <glib-object.h>\n#include <gio/gio.h>\n#include <json-glib/json-glib.h>\n")
|
||||
output_header_file.write("#include <glib.h>\n#include <glib-object.h>\n#include <gio/gio.h>\n#include <json-glib/json-glib.h>\n\n")
|
||||
|
||||
output_header_file.write(f"#define FRIDA_VERSION \"{api.frida_version}\"\n\n")
|
||||
|
||||
for name, value in zip(['MAJOR', 'MINOR', 'MICRO', 'NANO'], api.frida_version_components):
|
||||
output_header_file.write(f"#define FRIDA_{name}_VERSION {value}\n")
|
||||
|
||||
output_header_file.write("""
|
||||
#define FRIDA_CHECK_VERSION(maj, min, mic) \\
|
||||
(FRIDA_CURRENT_VERSION >= FRIDA_VERSION_ENCODE ((maj), (min), (mic)))
|
||||
|
||||
#define FRIDA_CURRENT_VERSION \\
|
||||
FRIDA_VERSION_ENCODE ( \\
|
||||
FRIDA_MAJOR_VERSION, \\
|
||||
FRIDA_MINOR_VERSION, \\
|
||||
FRIDA_MICRO_VERSION)
|
||||
|
||||
#define FRIDA_VERSION_ENCODE(maj, min, mic) \\
|
||||
(((maj) * 1000000U) + ((min) * 1000U) + (mic))
|
||||
""")
|
||||
|
||||
output_header_file.write("\nG_BEGIN_DECLS\n")
|
||||
|
||||
@@ -205,7 +238,9 @@ def emit_gir(api: ApiSpec, core_gir: str, base_gir: str, output_dir: Path) -> st
|
||||
for elem in core_elements + base_elements:
|
||||
if tag_name == "class":
|
||||
for child in list(elem):
|
||||
if child.tag == CORE_TAG_FIELD or child.get("name").startswith("_"):
|
||||
if (child.tag == CORE_TAG_IMPLEMENTS and child.get("name") == "FridaBase.AgentMessageSink") \
|
||||
or child.tag == CORE_TAG_FIELD \
|
||||
or child.get("name").startswith("_"):
|
||||
elem.remove(child)
|
||||
merged_namespace.append(elem)
|
||||
|
||||
@@ -217,6 +252,7 @@ def emit_gir(api: ApiSpec, core_gir: str, base_gir: str, output_dir: Path) -> st
|
||||
result = ET.tostring(merged_root,
|
||||
encoding="unicode",
|
||||
xml_declaration=True)
|
||||
result = result.replace("FridaBase.", "Frida.")
|
||||
with OutputFile(output_dir / f"Frida-{api.version}.gir") as output_gir:
|
||||
output_gir.write(result)
|
||||
|
||||
@@ -267,7 +303,7 @@ def emit_vapi(api, output_dir):
|
||||
output_deps_file.write("gobject-2.0\n")
|
||||
output_deps_file.write("gio-2.0\n")
|
||||
|
||||
def parse_api(api_version, toplevel_code, core_header, core_vapi, base_header, base_vapi):
|
||||
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
|
||||
|
||||
all_enum_names = [m.group(1) for m in re.finditer(r"^\t+public\s+enum\s+(\w+)\s+", toplevel_code + "\n" + base_vapi, re.MULTILINE)]
|
||||
@@ -383,7 +419,7 @@ def parse_api(api_version, toplevel_code, core_header, core_vapi, base_header, b
|
||||
if delegate_cname not in seen_cdelegates:
|
||||
seen_cdelegates.add(delegate_cname)
|
||||
object_type.c_delegate_typedefs.append(beautify_cprototype(d.group(0)))
|
||||
if object_type.kind == 'interface' and object_type.name != "Injector":
|
||||
if object_type.kind == 'interface':
|
||||
for m in re.finditer("^(struct _" + object_type.c_name + "Iface {[^}]+};)$", all_headers, re.MULTILINE):
|
||||
object_type.c_iface_definition = beautify_cinterface(m.group(1))
|
||||
|
||||
@@ -460,7 +496,7 @@ def parse_api(api_version, toplevel_code, core_header, core_vapi, base_header, b
|
||||
m = re.search(r"^[\w\*]+ frida_{}.+?;".format(f.name), all_headers, re.MULTILINE | re.DOTALL)
|
||||
f.c_prototype = beautify_cprototype(m.group(0))
|
||||
|
||||
return ApiSpec(api_version, object_types, functions, enum_types, error_types)
|
||||
return ApiSpec(frida_version, frida_version_components, api_version, object_types, functions, enum_types, error_types)
|
||||
|
||||
def function_is_public(name):
|
||||
return not name.startswith("_") and \
|
||||
@@ -474,7 +510,8 @@ def function_is_public(name):
|
||||
"parse_control_address",
|
||||
"parse_cluster_address",
|
||||
"parse_socket_address",
|
||||
"negotiate_connection"
|
||||
"negotiate_connection",
|
||||
"check_kernel_version",
|
||||
]
|
||||
|
||||
def parse_vala_object_types(source) -> List[ApiObjectType]:
|
||||
@@ -485,6 +522,8 @@ def parse_vapi_functions(vapi) -> List[ApiFunction]:
|
||||
|
||||
@dataclass
|
||||
class ApiSpec:
|
||||
frida_version: str
|
||||
frida_version_components: List[int]
|
||||
version: str
|
||||
object_types: List[ApiObjectType]
|
||||
functions: List[ApiFunction]
|
||||
|
||||
+10
-2
@@ -3,6 +3,11 @@ fs = import('fs')
|
||||
generate_api = [python, files('generate.py')]
|
||||
|
||||
generate_api_args = [
|
||||
frida_version,
|
||||
frida_major_version.to_string(),
|
||||
frida_minor_version.to_string(),
|
||||
frida_micro_version.to_string(),
|
||||
frida_nano_version.to_string(),
|
||||
api_version,
|
||||
core_build_dir / 'frida-core.h',
|
||||
core_build_dir / core_gir_name,
|
||||
@@ -34,7 +39,10 @@ core_public_h = core_api[0]
|
||||
core_public_gir = core_api[1]
|
||||
core_public_vapi = [core_api[2], core_api[3]]
|
||||
|
||||
gio_gir = fs.copyfile('Gio-2.0.gir')
|
||||
install_glib_girs = not gi_dep.found() and not fs.exists(girdir / 'GLib-2.0.gir')
|
||||
glib_gir = fs.copyfile('GLib-2.0.gir', install: install_glib_girs, install_dir: girdir)
|
||||
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)
|
||||
|
||||
core_public_vapi_stamp = custom_target('frida-core-vapi-stamp',
|
||||
input: core_public_vapi,
|
||||
@@ -89,7 +97,7 @@ installed_variables = {
|
||||
}
|
||||
|
||||
core_public_dep = declare_dependency(
|
||||
sources: [core_public_h, core_public_gir, gio_gir, core_public_vapi_stamp, core_public_extra_headers],
|
||||
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,
|
||||
|
||||
@@ -465,7 +465,8 @@ namespace Frida {
|
||||
|
||||
public async void load_script (AgentScriptId script_id, Cancellable? cancellable) throws Error, IOError {
|
||||
check_open ();
|
||||
get_script (script_id).load ();
|
||||
var script = get_script (script_id);
|
||||
yield script.load (cancellable);
|
||||
}
|
||||
|
||||
public async void eternalize_script (AgentScriptId script_id, Cancellable? cancellable) throws Error, IOError {
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def main(argv):
|
||||
output_dir, priv_dir, input_dir, npm = [Path(d).resolve() for d in argv[1:]]
|
||||
|
||||
try:
|
||||
generate_runtime(output_dir, priv_dir, input_dir, npm)
|
||||
except Exception as e:
|
||||
print(e, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def generate_runtime(output_dir, priv_dir, input_dir, npm):
|
||||
priv_dir.mkdir(exist_ok=True)
|
||||
|
||||
shutil.copy(input_dir / "package.json", priv_dir)
|
||||
shutil.copy(input_dir / "package-lock.json", priv_dir)
|
||||
|
||||
runtime_reldir = Path("script-runtime")
|
||||
runtime_srcdir = input_dir / runtime_reldir
|
||||
runtime_intdir = priv_dir / runtime_reldir
|
||||
if runtime_intdir.exists():
|
||||
shutil.rmtree(runtime_intdir)
|
||||
shutil.copytree(runtime_srcdir, runtime_intdir)
|
||||
|
||||
subprocess.run([npm, "install"], capture_output=True, cwd=priv_dir, check=True)
|
||||
|
||||
shutil.copy(priv_dir / "script-runtime.js", output_dir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
@@ -29,11 +29,11 @@ barebone_script_runtime = custom_target('frida-barebone-script-runtime',
|
||||
],
|
||||
command: [
|
||||
python,
|
||||
files('generate-script-runtime.py'),
|
||||
meson.current_build_dir(),
|
||||
compile_js_agent,
|
||||
'@OUTPUT@',
|
||||
'@PRIVATE_DIR@',
|
||||
meson.current_source_dir(),
|
||||
npm,
|
||||
'@INPUT@',
|
||||
],
|
||||
)
|
||||
backend_sources += custom_target('frida-data-barebone',
|
||||
|
||||
Generated
+451
-442
@@ -1,494 +1,503 @@
|
||||
{
|
||||
"name": "frida-barebone-runtime",
|
||||
"name": "frida-barebone-script-runtime",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "frida-barebone-runtime",
|
||||
"name": "frida-barebone-script-runtime",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@types/frida-gum": "^18.3.2",
|
||||
"frida-compile": "^16.2.1"
|
||||
"@types/frida-gum": "^19.0.0",
|
||||
"esbuild": "^0.25.5",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@frida/assert": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@frida/assert/-/assert-3.0.2.tgz",
|
||||
"integrity": "sha512-JXJq5SbXGrM5EkjrZKfRmB29zOoEOix02NC6A5TSJ+C1GE/X051EinJJsuOO2pEOx7KZwpvAHvS0WXW0+levKg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/base64-js": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@frida/base64-js/-/base64-js-2.0.3.tgz",
|
||||
"integrity": "sha512-2w0F+1TynOTCZ/v7du9LdHPWwq0lJhazjo2fF9upMyQmA1zHetT14fLuQ1v/6T0qPgyeEGkiSrybstU8EsgeUA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@frida/buffer": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@frida/buffer/-/buffer-7.0.4.tgz",
|
||||
"integrity": "sha512-RxQ1lZRRiCJj7nhcCiD8xeJx0NsLpGGnjqsmTg7jShGmbnVFMN5W7+J+3gqdPSQhc/IxNBIWc6zRXVp4+qnYHg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dependencies": {
|
||||
"base64-js": "^1.5.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@frida/crosspath": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@frida/crosspath/-/crosspath-3.0.0.tgz",
|
||||
"integrity": "sha512-bNdO1spIPD2P40XtK89N49oZpJhstdlnkJZcD4yJ17jrdkm9Ctu0sd9MIEX6Z8Tm8ydhVJBAOMEKl9/R27onAQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^17.0.36"
|
||||
},
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=14.9.0"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@frida/diagnostics_channel": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@frida/diagnostics_channel/-/diagnostics_channel-1.0.0.tgz",
|
||||
"integrity": "sha512-mYX1jp/5Bpk24tHArJNx65iCk7qSuV8YJkdU0gFNVtJUXxfV8BG5WuPa4mL+ynxsbWWpsg/cwKZbLAepYKTdQQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/events": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@frida/events/-/events-4.0.4.tgz",
|
||||
"integrity": "sha512-qJVQ6VWHf9sjUKuiJzoCAC00frbpcwxeYfvQ+PP9LU/d70j+QvjWgYe98Qa3ekLaBU6r/AvWm8ThKCDUCLWrQQ==",
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=0.8.x"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@frida/http": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@frida/http/-/http-4.0.2.tgz",
|
||||
"integrity": "sha512-cvkc7ex7GmVXVOWqtjXKBWUUbYEBgpNRKZbEEoMeI8KiIs8zejKwg+N7rx7296Ao+EP3+xcUr4wBVr3xLaUVfQ==",
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"http-parser-js": "^0.5.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@frida/http-parser-js": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@frida/http-parser-js/-/http-parser-js-1.0.0.tgz",
|
||||
"integrity": "sha512-2nMrNXt/OeTlWbqnE8AH4Sfz4I2+BGoN206dzKEyC/g2svtn83Xu+zuv/V3TkwrA27s26Mcy84ZwsXeNlqNxUQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/https": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@frida/https/-/https-1.0.0.tgz",
|
||||
"integrity": "sha512-OiqQ6qsALcWOktRLq07oJ0i6sH8eX6MXb/MdZS1qVKDRf6wchH4Pjn6fiLB+pt/OlYbggk+DOfpHwSdjTwuHMQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/ieee754": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@frida/ieee754/-/ieee754-2.0.2.tgz",
|
||||
"integrity": "sha512-wlcUebnne4ENN7GDr5pTH598ZDLMVOsh0FjenxeVOe6u7ewZkz9gGRnLnZKJAm9kl5G6XhdxhI0cSXVQK/rQUw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@frida/net": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@frida/net/-/net-4.0.2.tgz",
|
||||
"integrity": "sha512-qQRe7hQ+ZfCcG/SE3P1TRqQ9bmuK/T7wPCYaT4z56rBPWAxsaQbQHpX4fR6OrFaSDr7X0xJLsTbdIp9hGhhLZg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/os": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@frida/os/-/os-1.0.2.tgz",
|
||||
"integrity": "sha512-3ISAiGNiyIya3QN2EHBCz1wqP0enTdSxP99wUeroeh8+AQRmgoOr/5TRnrVry8pe378anay3fmV/tdUMMSkehQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/path": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@frida/path/-/path-2.0.3.tgz",
|
||||
"integrity": "sha512-2RQy36QatoC846fzBhBhV8sXMsSOBGoYvwTHeaE1zUdz7F4RNScP4QEekTTooBYWYX/XjiF36KQpYAzc9OYFtg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/process": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@frida/process/-/process-1.2.1.tgz",
|
||||
"integrity": "sha512-nvCu22DstFW2ttGFtOKekHM7vnjbZm+XgtvavOt427GNT6uV7k0JYK9tnMbcLMRWv57DG6udAmuJlWs8Paq1ag==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/punycode": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@frida/punycode/-/punycode-3.0.0.tgz",
|
||||
"integrity": "sha512-XVSDY2KamDs1D5/fTVgHcOSNxdU4kTboxzqJMBbTjcQC7XScIT9c0EfbwKCq7Kci6gWQdsHSCr7lU+9Oc4KAdg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/querystring": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@frida/querystring/-/querystring-1.0.0.tgz",
|
||||
"integrity": "sha512-15m1fOZPmoO/vWlgPJrG/J9/BJDz6a2/JpVGpS8ynNzo+fBhTznaStX5nHxUs24mVTqh/OqLo0EiYJM5WWHXxg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/readable-stream": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@frida/readable-stream/-/readable-stream-4.1.3.tgz",
|
||||
"integrity": "sha512-ntGUFmi+CryRGRJIK13a/VST2Ad19uivbln8Xd92vKPAARq+6vMIASDyZIqyl5BLRccfiyCHdYgrgQ6RI5rUig==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/reserved-words": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@frida/reserved-words/-/reserved-words-1.0.0.tgz",
|
||||
"integrity": "sha512-2yG/XxJlsGlk/mm6eZTb4OAaQEhkTI2qaFfZFtAsrA/XuCpuMWkS4y/guyBlsRu4hAuhK2HPmNM8+OLLK1zM9Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/stream": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@frida/stream/-/stream-1.0.2.tgz",
|
||||
"integrity": "sha512-4OuaC1ztmEKgTq3WeBhsy8Oq+AwW9n9cYnvLklcC9jwD93AEwgbWpecLlxJCVuALvTMdhKPg0nQVfyGYP/i9Bw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@frida/readable-stream": "^4.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@frida/string_decoder": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@frida/string_decoder/-/string_decoder-2.0.0.tgz",
|
||||
"integrity": "sha512-in371tYZMHQiW9HF5MS3JDw6Ao6tyBoq34UWy2rzOswYyMG1rpizh85ofi/yVkxDiaqybEZefxzkVittpPGT6g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/terser": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@frida/terser/-/terser-1.0.0.tgz",
|
||||
"integrity": "sha512-59h9WuNzD1Rx/zwoWqQ/FW/4Y/Q3R91Eng2hEwdHapqiTDvtKbZ08F6CynCR7ZVinrh4tLYsF46AtVPTz1ys9g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
"acorn": "^8.5.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"bin": {
|
||||
"terser": "bin/terser"
|
||||
},
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@frida/terser/node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/timers": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@frida/timers/-/timers-3.0.0.tgz",
|
||||
"integrity": "sha512-3b+0igv10aT8TMxefrTAd06rActqbxJLY2Xkkq9vYcPBffB/yHszl0NYIp/5ko8WC3ecDYPU6bQiY6fjs72zTA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/tty": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@frida/tty/-/tty-1.0.0.tgz",
|
||||
"integrity": "sha512-p/kjLnKYxEAB1MdYP8+5rKv9CsHzyA+0jg9BcGETzjQVKHHcroHDULRxDYUh+DC7qs6cpX8QdDQh9E+a6ydgsQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/url": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@frida/url/-/url-1.0.2.tgz",
|
||||
"integrity": "sha512-ZKunbKJHMr8w2Eb/5K1avy0MzK1B998S17wYXNv3RmzBGxMm8S5T0F3qEpRxkU7/72P8m4izyQU87fWl+FjQsQ==",
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@frida/punycode": "^3.0.0",
|
||||
"@frida/querystring": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@frida/util": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@frida/util/-/util-1.0.3.tgz",
|
||||
"integrity": "sha512-htcG3uDiRXv89ERVNNYhfase39kJ2X75ZARfrYcYEtJLFEsSk0nemM1YnEIR4CjrHvdvkWHrwgKkS+acOyoNEg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@frida/vm": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@frida/vm/-/vm-2.0.0.tgz",
|
||||
"integrity": "sha512-7fsjLWscZT5odNIBtg6qbLNI+vAk1xmii6H5W2kaYkMYt0vRohQEcDSUWacA+eaWlu5SvMjZI82Yibj/3G9pJw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
},
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz",
|
||||
"integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==",
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
|
||||
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.18",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
|
||||
"integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "1.4.14"
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||
"dev": true
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
|
||||
"integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
|
||||
"integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
|
||||
"integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
|
||||
"integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/frida-gum": {
|
||||
"version": "18.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.3.2.tgz",
|
||||
"integrity": "sha512-bso8bJRxrhchNRrOMmT18st/THIdjzxFdcMtJ/gGSYSknJeerQ8ArBwBtlWt/f3pqVVs1Mz1Fg6lvUdjXswa+g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "17.0.45",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
|
||||
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.8.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
|
||||
"integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
|
||||
"version": "19.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-19.0.0.tgz",
|
||||
"integrity": "sha512-qViK1KwvPEAkajwSUv0wpZvZLiX8xO4JFFCh8dfYZfzSyOEfoE0KliCP/nJSKIKG6N3hXnPowrvPufjs5fLKAA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
|
||||
"integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "9.5.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
|
||||
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.20.0 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/frida-compile": {
|
||||
"version": "16.2.1",
|
||||
"resolved": "https://registry.npmjs.org/frida-compile/-/frida-compile-16.2.1.tgz",
|
||||
"integrity": "sha512-be/U5h8l0cb0b+fWeZclhfw1s2xER8fPOjnTU4rZF8oh5GkB0BeAEaApwfGrdmeZkVNdUhDLXud7DqEljHRWmA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@frida/assert": "^3.0.1",
|
||||
"@frida/base64-js": "^2.0.3",
|
||||
"@frida/buffer": "^7.0.4",
|
||||
"@frida/crosspath": "^3.0.0",
|
||||
"@frida/diagnostics_channel": "^1.0.0",
|
||||
"@frida/events": "^4.0.4",
|
||||
"@frida/http": "^4.0.2",
|
||||
"@frida/http-parser-js": "^1.0.0",
|
||||
"@frida/https": "^1.0.0",
|
||||
"@frida/ieee754": "^2.0.2",
|
||||
"@frida/net": "^4.0.1",
|
||||
"@frida/os": "^1.0.0",
|
||||
"@frida/path": "^2.0.3",
|
||||
"@frida/process": "^1.2.1",
|
||||
"@frida/punycode": "^3.0.0",
|
||||
"@frida/querystring": "^1.0.0",
|
||||
"@frida/readable-stream": "^4.1.3",
|
||||
"@frida/reserved-words": "^1.0.0",
|
||||
"@frida/stream": "^1.0.2",
|
||||
"@frida/string_decoder": "^2.0.0",
|
||||
"@frida/terser": "^1.0.0",
|
||||
"@frida/timers": "^3.0.0",
|
||||
"@frida/tty": "^1.0.0",
|
||||
"@frida/url": "^1.0.2",
|
||||
"@frida/util": "^1.0.3",
|
||||
"@frida/vm": "^2.0.0",
|
||||
"commander": "^9.4.0",
|
||||
"frida-fs": "^5.2.3",
|
||||
"typed-emitter": "^2.1.0"
|
||||
"node": ">=18"
|
||||
},
|
||||
"bin": {
|
||||
"frida-compile": "dist/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/frida-fs": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/frida-fs/-/frida-fs-5.2.3.tgz",
|
||||
"integrity": "sha512-jJhtNI86UqDkOtB72XR7Ia9KHudQUxIFIbZadS9kxrDU6Io1jLhoMNp++iW7n7MaDgFQLRR8VgNj9VViGzaydA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/http-parser-js": {
|
||||
"version": "0.5.8",
|
||||
"resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz",
|
||||
"integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-support": {
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.2.tgz",
|
||||
"integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/typed-emitter": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz",
|
||||
"integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==",
|
||||
"dev": true,
|
||||
"optionalDependencies": {
|
||||
"rxjs": "*"
|
||||
"@esbuild/aix-ppc64": "0.25.5",
|
||||
"@esbuild/android-arm": "0.25.5",
|
||||
"@esbuild/android-arm64": "0.25.5",
|
||||
"@esbuild/android-x64": "0.25.5",
|
||||
"@esbuild/darwin-arm64": "0.25.5",
|
||||
"@esbuild/darwin-x64": "0.25.5",
|
||||
"@esbuild/freebsd-arm64": "0.25.5",
|
||||
"@esbuild/freebsd-x64": "0.25.5",
|
||||
"@esbuild/linux-arm": "0.25.5",
|
||||
"@esbuild/linux-arm64": "0.25.5",
|
||||
"@esbuild/linux-ia32": "0.25.5",
|
||||
"@esbuild/linux-loong64": "0.25.5",
|
||||
"@esbuild/linux-mips64el": "0.25.5",
|
||||
"@esbuild/linux-ppc64": "0.25.5",
|
||||
"@esbuild/linux-riscv64": "0.25.5",
|
||||
"@esbuild/linux-s390x": "0.25.5",
|
||||
"@esbuild/linux-x64": "0.25.5",
|
||||
"@esbuild/netbsd-arm64": "0.25.5",
|
||||
"@esbuild/netbsd-x64": "0.25.5",
|
||||
"@esbuild/openbsd-arm64": "0.25.5",
|
||||
"@esbuild/openbsd-x64": "0.25.5",
|
||||
"@esbuild/sunos-x64": "0.25.5",
|
||||
"@esbuild/win32-arm64": "0.25.5",
|
||||
"@esbuild/win32-ia32": "0.25.5",
|
||||
"@esbuild/win32-x64": "0.25.5"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
"name": "frida-barebone-script-runtime",
|
||||
"version": "1.0.0",
|
||||
"description": "Script runtime used by Frida's Barebone backend",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"prepare": "npm run build",
|
||||
"build": "frida-compile script-runtime/entrypoint.ts -o script-runtime.js -c"
|
||||
"build": "esbuild script-runtime/entrypoint.ts --outfile=script-runtime.js --bundle --platform=node --target=es2022 --minify",
|
||||
"check": "tsc",
|
||||
"watch": "tsc -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/frida-gum": "^18.3.2",
|
||||
"frida-compile": "^16.2.1"
|
||||
"@types/frida-gum": "^19.0.0",
|
||||
"esbuild": "^0.25.5",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
@@ -208,7 +208,10 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
~CompilationAssets () {
|
||||
rmtree (workdir);
|
||||
try {
|
||||
FS.rmtree (workdir);
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private static string make_main_rs (string code, Machine machine) {
|
||||
@@ -428,23 +431,4 @@ namespace Frida.Barebone {
|
||||
yield file.replace_contents_async (content.data, null, false, FileCreateFlags.NONE, cancellable, null);
|
||||
return file;
|
||||
}
|
||||
|
||||
private void rmtree (File dir) {
|
||||
try {
|
||||
var enumerator = dir.enumerate_children (FileAttribute.STANDARD_NAME, NOFOLLOW_SYMLINKS);
|
||||
FileInfo? info;
|
||||
File? child;
|
||||
while (enumerator.iterate (out info, out child) && info != null) {
|
||||
if (info == null)
|
||||
continue;
|
||||
if (info.get_file_type () == DIRECTORY)
|
||||
rmtree (child);
|
||||
else
|
||||
child.delete ();
|
||||
}
|
||||
|
||||
dir.delete ();
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,24 +210,6 @@ Object.defineProperties(globalThis, {
|
||||
}
|
||||
}
|
||||
},
|
||||
ObjC: {
|
||||
enumerable: true,
|
||||
value: {
|
||||
available: false
|
||||
}
|
||||
},
|
||||
Swift: {
|
||||
enumerable: true,
|
||||
value: {
|
||||
available: false
|
||||
}
|
||||
},
|
||||
Java: {
|
||||
enumerable: true,
|
||||
value: {
|
||||
available: false
|
||||
}
|
||||
},
|
||||
console: {
|
||||
enumerable: true,
|
||||
value: new Console()
|
||||
@@ -278,6 +260,8 @@ Object.defineProperties(Process, {
|
||||
base: NULL,
|
||||
size: 0x1000,
|
||||
path: "/kernel",
|
||||
ensureInitialized() {
|
||||
},
|
||||
enumerateImports() {
|
||||
return [];
|
||||
},
|
||||
@@ -290,11 +274,23 @@ Object.defineProperties(Process, {
|
||||
enumerateRanges() {
|
||||
return [];
|
||||
},
|
||||
enumerateSections() {
|
||||
return [];
|
||||
},
|
||||
enumerateDependencies() {
|
||||
return [];
|
||||
},
|
||||
findExportByName() {
|
||||
return null;
|
||||
},
|
||||
getExportByName(exportName) {
|
||||
throw new Error(`unable to find export '${exportName}'`);
|
||||
getExportByName(name) {
|
||||
throw new Error(`unable to find export '${name}'`);
|
||||
},
|
||||
findSymbolByName() {
|
||||
return null;
|
||||
},
|
||||
getSymbolByName(name) {
|
||||
throw new Error(`unable to find symbol '${name}'`);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
+162
-38
@@ -72,7 +72,7 @@ namespace Frida {
|
||||
private QuickJS.Value int64_func = QuickJS.Undefined;
|
||||
private QuickJS.Value uint64_func = QuickJS.Undefined;
|
||||
|
||||
private Gee.ArrayList<QuickJS.Value?> entrypoints = new Gee.ArrayList<QuickJS.Value?> ();
|
||||
private Gee.Queue<Entrypoint> entrypoints = new Gee.ArrayQueue<Entrypoint> ();
|
||||
private Gee.Map<string, Asset> assets = new Gee.HashMap<string, Asset> ();
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
@@ -328,6 +328,8 @@ namespace Frida {
|
||||
foreach (var val in values)
|
||||
ctx.free_value (val);
|
||||
|
||||
entrypoints.clear ();
|
||||
|
||||
QuickJS.Atom atoms[] = {
|
||||
address_key,
|
||||
base_key,
|
||||
@@ -376,48 +378,65 @@ namespace Frida {
|
||||
yield;
|
||||
}
|
||||
|
||||
public void load () {
|
||||
foreach (QuickJS.Value? entrypoint in entrypoints) {
|
||||
var result = ctx.eval_function (entrypoint);
|
||||
if (result.is_exception ())
|
||||
public async void load (Cancellable? cancellable) throws IOError {
|
||||
Entrypoint? entrypoint;
|
||||
while ((entrypoint = entrypoints.poll ()) != null) {
|
||||
var result = ctx.eval_function (ctx.dup_value (entrypoint.callable));
|
||||
if (result.is_exception ()) {
|
||||
catch_and_emit ();
|
||||
ctx.free_value (result);
|
||||
|
||||
if (runtime_obj.is_undefined ()) {
|
||||
runtime_obj = global.get_property_str (ctx, "$rt");
|
||||
if (!runtime_obj.is_undefined ()) {
|
||||
dispatch_exception_func = runtime_obj.get_property_str (ctx, "dispatchException");
|
||||
assert (!dispatch_exception_func.is_undefined ());
|
||||
|
||||
dispatch_message_func = runtime_obj.get_property_str (ctx, "dispatchMessage");
|
||||
assert (!dispatch_message_func.is_undefined ());
|
||||
|
||||
var native_pointer_instance = global.get_property_str (ctx, "NULL");
|
||||
assert (!native_pointer_instance.is_undefined ());
|
||||
var native_pointer_proto = native_pointer_instance.get_prototype (ctx);
|
||||
|
||||
var ir_proto = ctx.make_object_proto (native_pointer_proto);
|
||||
add_cfunc (ir_proto, "replace", on_invocation_retval_replace, 1);
|
||||
ctx.set_class_proto (invocation_retval_class, ir_proto);
|
||||
|
||||
ctx.free_value (native_pointer_proto);
|
||||
ctx.free_value (native_pointer_instance);
|
||||
|
||||
ptr_func = global.get_property_str (ctx, "ptr");
|
||||
assert (!ptr_func.is_undefined ());
|
||||
|
||||
int64_func = global.get_property_str (ctx, "int64");
|
||||
assert (!int64_func.is_undefined ());
|
||||
|
||||
uint64_func = global.get_property_str (ctx, "uint64");
|
||||
assert (!uint64_func.is_undefined ());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entrypoint.kind == ESM) {
|
||||
var op = new PromiseWaitOperation (this, result);
|
||||
var r = yield op.perform (cancellable);
|
||||
if (!r.error.is_null ())
|
||||
on_unhandled_exception (r.error);
|
||||
} else {
|
||||
ctx.free_value (result);
|
||||
}
|
||||
|
||||
maybe_bind_runtime ();
|
||||
}
|
||||
|
||||
perform_pending_io ();
|
||||
}
|
||||
|
||||
private void maybe_bind_runtime () {
|
||||
if (!runtime_obj.is_undefined ())
|
||||
return;
|
||||
|
||||
runtime_obj = global.get_property_str (ctx, "$rt");
|
||||
if (runtime_obj.is_undefined ())
|
||||
return;
|
||||
|
||||
dispatch_exception_func = runtime_obj.get_property_str (ctx, "dispatchException");
|
||||
assert (!dispatch_exception_func.is_undefined ());
|
||||
|
||||
dispatch_message_func = runtime_obj.get_property_str (ctx, "dispatchMessage");
|
||||
assert (!dispatch_message_func.is_undefined ());
|
||||
|
||||
var native_pointer_instance = global.get_property_str (ctx, "NULL");
|
||||
assert (!native_pointer_instance.is_undefined ());
|
||||
var native_pointer_proto = native_pointer_instance.get_prototype (ctx);
|
||||
|
||||
var ir_proto = ctx.make_object_proto (native_pointer_proto);
|
||||
add_cfunc (ir_proto, "replace", on_invocation_retval_replace, 1);
|
||||
ctx.set_class_proto (invocation_retval_class, ir_proto);
|
||||
|
||||
ctx.free_value (native_pointer_proto);
|
||||
ctx.free_value (native_pointer_instance);
|
||||
|
||||
ptr_func = global.get_property_str (ctx, "ptr");
|
||||
assert (!ptr_func.is_undefined ());
|
||||
|
||||
int64_func = global.get_property_str (ctx, "int64");
|
||||
assert (!int64_func.is_undefined ());
|
||||
|
||||
uint64_func = global.get_property_str (ctx, "uint64");
|
||||
assert (!uint64_func.is_undefined ());
|
||||
}
|
||||
|
||||
public void post (string json, Bytes? data) {
|
||||
var json_val = ctx.make_string (json);
|
||||
var data_val = (data != null) ? ctx.make_array_buffer (data.get_data ()) : QuickJS.Null;
|
||||
@@ -495,7 +514,7 @@ namespace Frida {
|
||||
throw_malformed_package ();
|
||||
|
||||
var val = compile_module (entrypoint);
|
||||
entrypoints.add (val);
|
||||
entrypoints.offer (new Entrypoint (this, val, ESM));
|
||||
|
||||
string rest = raw_assets[assets_offset:];
|
||||
if (rest.has_prefix (delimiter_marker))
|
||||
@@ -507,7 +526,7 @@ namespace Frida {
|
||||
}
|
||||
} else {
|
||||
var val = compile_script (source, name);
|
||||
entrypoints.add (val);
|
||||
entrypoints.offer (new Entrypoint (this, val, PLAIN));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -708,6 +727,111 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private class Entrypoint {
|
||||
private weak BareboneScript script;
|
||||
public QuickJS.Value callable;
|
||||
public Kind kind;
|
||||
|
||||
public enum Kind {
|
||||
PLAIN,
|
||||
ESM
|
||||
}
|
||||
|
||||
public Entrypoint (BareboneScript script, QuickJS.Value callable, Kind kind) {
|
||||
this.script = script;
|
||||
this.callable = callable;
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
~Entrypoint () {
|
||||
script.ctx.free_value (callable);
|
||||
}
|
||||
}
|
||||
|
||||
private class PromiseWaitOperation {
|
||||
private BareboneScript script;
|
||||
private QuickJS.Value promise;
|
||||
|
||||
private enum Magic {
|
||||
ON_SUCCESS,
|
||||
ON_FAILURE,
|
||||
}
|
||||
|
||||
public PromiseWaitOperation (BareboneScript script, QuickJS.Value promise) {
|
||||
this.script = script;
|
||||
this.promise = promise;
|
||||
}
|
||||
|
||||
~PromiseWaitOperation () {
|
||||
script.ctx.free_value (promise);
|
||||
}
|
||||
|
||||
public async Result perform (Cancellable? cancellable) throws IOError {
|
||||
var res = new Result (script, perform.callback);
|
||||
|
||||
unowned QuickJS.Context ctx = script.ctx;
|
||||
|
||||
var data = ctx.make_object ();
|
||||
data.set_opaque (res);
|
||||
|
||||
var on_success = ctx.make_cfunction_data (on_settled, 1, Magic.ON_SUCCESS, { data });
|
||||
var on_failure = ctx.make_cfunction_data (on_settled, 1, Magic.ON_FAILURE, { data });
|
||||
|
||||
var then_func = promise.get_property_str (ctx, "then");
|
||||
var catch_func = promise.get_property_str (ctx, "catch");
|
||||
|
||||
ctx.free_value (then_func.call (ctx, promise, { on_success }));
|
||||
ctx.free_value (catch_func.call (ctx, promise, { on_failure }));
|
||||
|
||||
ctx.free_value (catch_func);
|
||||
ctx.free_value (then_func);
|
||||
|
||||
ctx.free_value (on_failure);
|
||||
ctx.free_value (on_success);
|
||||
|
||||
script.perform_pending_io ();
|
||||
yield;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static QuickJS.Value on_settled (QuickJS.Context ctx, QuickJS.Value this_val, QuickJS.Value[] argv, int magic,
|
||||
QuickJS.Value[] data) {
|
||||
QuickJS.ClassID cid;
|
||||
unowned Result res = (Result) data[0].get_any_opaque (out cid);
|
||||
|
||||
if (magic == Magic.ON_SUCCESS)
|
||||
res.val = ctx.dup_value (argv[0]);
|
||||
else
|
||||
res.error = ctx.dup_value (argv[0]);
|
||||
|
||||
var source = new IdleSource ();
|
||||
source.set_callback ((owned) res.on_complete);
|
||||
source.attach (MainContext.get_thread_default ());
|
||||
|
||||
return QuickJS.Undefined;
|
||||
}
|
||||
|
||||
public class Result {
|
||||
public BareboneScript script;
|
||||
public SourceFunc on_complete;
|
||||
|
||||
public QuickJS.Value val = QuickJS.Null;
|
||||
public QuickJS.Value error = QuickJS.Null;
|
||||
|
||||
public Result (BareboneScript script, owned SourceFunc on_complete) {
|
||||
this.script = script;
|
||||
this.on_complete = (owned) on_complete;
|
||||
}
|
||||
|
||||
~Result () {
|
||||
unowned QuickJS.Context ctx = script.ctx;
|
||||
ctx.free_value (val);
|
||||
ctx.free_value (error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class NativeCallbackHandler : Object, Barebone.CallbackHandler {
|
||||
public uint arity {
|
||||
get { return _arity; }
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022"],
|
||||
"module": "Node16",
|
||||
"strict": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["script-runtime/**/*.ts"]
|
||||
}
|
||||
@@ -1,469 +0,0 @@
|
||||
import * as crosspath from "@frida/crosspath";
|
||||
import type * as backend from "frida-compile";
|
||||
import {
|
||||
makeDefaultCompilerOptions,
|
||||
build as _build,
|
||||
watch as _watch,
|
||||
queryDefaultAssets as _queryDefaultAssets,
|
||||
} from "frida-compile";
|
||||
import ts from "frida-compile/ext/typescript.js";
|
||||
import fridaFs from "frida-fs";
|
||||
import fs from "fs";
|
||||
|
||||
const { DiagnosticCategory, FileWatcherEventKind } = ts;
|
||||
|
||||
const { S_IFDIR } = fs.constants;
|
||||
|
||||
export const compilerRoot = "/frida-compile";
|
||||
export const compilerNodeModules = compilerRoot + "/node_modules";
|
||||
|
||||
export const agentDirectories = new Set<string>(__agentDirectories__);
|
||||
export const agentFiles = new Map<string, string>(__agentFiles__);
|
||||
const cachedSourceFiles = new Map<string, ts.SourceFile>();
|
||||
|
||||
declare const __agentDirectories__: string[];
|
||||
declare const __agentFiles__: [string, string][];
|
||||
|
||||
const pendingDiagnostics: ts.Diagnostic[] = [];
|
||||
|
||||
const fileWatchers = new Map<WatcherId, WatcherCallback>();
|
||||
let nextWatcherId: WatcherId = 1;
|
||||
|
||||
populateSourceFileCache();
|
||||
|
||||
function populateSourceFileCache() {
|
||||
const config = makeDefaultCompilerOptions();
|
||||
for (const [name, contents] of agentFiles) {
|
||||
if (name.endsWith(".d.ts")) {
|
||||
const sf = ts.createSourceFile(name, contents, config.target!);
|
||||
ts.prebindSourceFile(sf, config);
|
||||
cachedSourceFiles.set(name, sf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function init(): void {
|
||||
function onChange(message: WatchChangeMessage): void {
|
||||
const callback = fileWatchers.get(message.id);
|
||||
callback?.(fileWatcherEventKindFromFileState(message.state));
|
||||
|
||||
recv("watch:change", onChange);
|
||||
}
|
||||
recv("watch:change", onChange);
|
||||
|
||||
const origCreateCompilerHostFromProgramHost = (ts as any).createCompilerHostFromProgramHost;
|
||||
(ts as any).createCompilerHostFromProgramHost = (...args: any[]): ts.CompilerHost => {
|
||||
const host = origCreateCompilerHostFromProgramHost(...args);
|
||||
patchCompilerHost(host);
|
||||
return host;
|
||||
};
|
||||
}
|
||||
|
||||
export function build(projectRoot: string, entrypoint: string, sourceMaps: backend.SourceMaps, compression: backend.Compression): string {
|
||||
const system = new FridaSystem(projectRoot);
|
||||
const assets = _queryDefaultAssets(projectRoot, system);
|
||||
|
||||
try {
|
||||
return _build({
|
||||
projectRoot,
|
||||
entrypoint,
|
||||
assets,
|
||||
system,
|
||||
sourceMaps,
|
||||
compression,
|
||||
onDiagnostic,
|
||||
onCompilerHostCreated: patchCompilerHost,
|
||||
});
|
||||
} finally {
|
||||
flushDiagnostics();
|
||||
}
|
||||
}
|
||||
|
||||
export function watch(projectRoot: string, entrypoint: string, sourceMaps: backend.SourceMaps, compression: backend.Compression): void {
|
||||
const system = new FridaSystem(projectRoot);
|
||||
const assets = _queryDefaultAssets(projectRoot, system);
|
||||
_watch({
|
||||
projectRoot,
|
||||
entrypoint,
|
||||
assets,
|
||||
system,
|
||||
sourceMaps,
|
||||
compression,
|
||||
onDiagnostic,
|
||||
})
|
||||
.on("compilationStarting", () => {
|
||||
send(["watch:compilation-starting"]);
|
||||
})
|
||||
.on("compilationFinished", () => {
|
||||
send(["watch:compilation-finished"]);
|
||||
})
|
||||
.on("bundleUpdated", bundle => {
|
||||
send(["watch:bundle-updated", bundle]);
|
||||
});
|
||||
}
|
||||
|
||||
function patchCompilerHost(host: ts.CompilerHost) {
|
||||
const origGetSourceFile = host.getSourceFile;
|
||||
host.getSourceFile = (fileName, ...args) => {
|
||||
const compilerPath = nativePathToCompilerPath(fileName);
|
||||
if (compilerPath !== null) {
|
||||
const sf = cachedSourceFiles.get(compilerPath);
|
||||
if (sf !== undefined) {
|
||||
return sf;
|
||||
}
|
||||
} else {
|
||||
const startPos = fileName.indexOf("/node_modules/@types/");
|
||||
if (startPos !== -1) {
|
||||
const builtinsPath = fileName.substring(startPos);
|
||||
const sf = cachedSourceFiles.get(builtinsPath);
|
||||
if (sf !== undefined) {
|
||||
let allowCache = true;
|
||||
|
||||
if (builtinsPath.startsWith("/node_modules/@types/frida-gum/")) {
|
||||
try {
|
||||
const hashOnDisk = Checksum.compute("sha256", File.readAllText(fileName));
|
||||
const hashInCache = Checksum.compute("sha256", agentFiles.get(sf.fileName)!);
|
||||
allowCache = hashOnDisk === hashInCache;
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (allowCache) {
|
||||
return sf;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return origGetSourceFile(fileName, ...args);
|
||||
};
|
||||
}
|
||||
|
||||
export { _build, _watch, _queryDefaultAssets };
|
||||
|
||||
type DiagnosticFile = [path: string, line: number, character: number];
|
||||
|
||||
function onDiagnostic(diagnostic: ts.Diagnostic): void {
|
||||
pendingDiagnostics.push(diagnostic);
|
||||
if (pendingDiagnostics.length === 1) {
|
||||
Script.nextTick(flushDiagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
function flushDiagnostics(): void {
|
||||
const pending = pendingDiagnostics.splice(0, pendingDiagnostics.length);
|
||||
if (pending.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
send([
|
||||
"diagnostics",
|
||||
pending.map(({
|
||||
category,
|
||||
code,
|
||||
file: tsFile,
|
||||
start,
|
||||
messageText
|
||||
}) => {
|
||||
let file: DiagnosticFile | null = null;
|
||||
if (tsFile !== undefined) {
|
||||
const { line, character } = ts.getLineAndCharacterOfPosition(tsFile, start!);
|
||||
file = [tsFile.fileName, line, character];
|
||||
}
|
||||
return [
|
||||
diagnosticCategoryToString(category),
|
||||
code,
|
||||
file,
|
||||
ts.flattenDiagnosticMessageText(messageText, "\n")
|
||||
];
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
function diagnosticCategoryToString(category: ts.DiagnosticCategory): string {
|
||||
switch (category) {
|
||||
case DiagnosticCategory.Warning:
|
||||
return "warning";
|
||||
case DiagnosticCategory.Error:
|
||||
return "error";
|
||||
case DiagnosticCategory.Suggestion:
|
||||
return "suggestion";
|
||||
case DiagnosticCategory.Message:
|
||||
return "message";
|
||||
}
|
||||
}
|
||||
|
||||
class FridaSystem implements ts.System {
|
||||
args = [];
|
||||
newLine = "\n";
|
||||
useCaseSensitiveFileNames = true;
|
||||
|
||||
#projectRoot: string;
|
||||
#projectNodeModules: string;
|
||||
|
||||
constructor(projectRoot: string) {
|
||||
this.#projectRoot = crosspath.ensurePosix(projectRoot);
|
||||
this.#projectNodeModules = crosspath.join(this.#projectRoot, "node_modules");
|
||||
}
|
||||
|
||||
write(s: string): void {
|
||||
}
|
||||
|
||||
writeOutputIsTTY(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
readFile(path: string, encoding?: string): string | undefined {
|
||||
let result: string | undefined;
|
||||
if (path.startsWith(this.#projectRoot)) {
|
||||
try {
|
||||
result = File.readAllText(path);
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (result === undefined) {
|
||||
const agentZipPath = this.#nativePathToAgentZipPath(path);
|
||||
if (agentZipPath !== null) {
|
||||
result = agentFiles.get(agentZipPath);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
getFileSize(path: string): number {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
writeFile(path: string, data: string, writeByteOrderMark?: boolean): void {
|
||||
File.writeAllText(path, data);
|
||||
}
|
||||
|
||||
watchFile(path: string, callback: ts.FileWatcherCallback, pollingInterval?: number, options?: ts.WatchOptions): ts.FileWatcher {
|
||||
if (path.startsWith(compilerRoot)) {
|
||||
return makeDummyWatcher();
|
||||
}
|
||||
|
||||
return makeFileWatcher(path, "file", pollingInterval ?? 0, kind => {
|
||||
callback(path, kind);
|
||||
});
|
||||
}
|
||||
|
||||
watchDirectory(path: string, callback: ts.DirectoryWatcherCallback, recursive?: boolean, options?: ts.WatchOptions): ts.FileWatcher {
|
||||
if (path.startsWith(compilerRoot)) {
|
||||
return makeDummyWatcher();
|
||||
}
|
||||
|
||||
return makeFileWatcher(path, "directory", 0, kind => {
|
||||
callback(path);
|
||||
});
|
||||
}
|
||||
|
||||
resolvePath(path: string): string {
|
||||
return path;
|
||||
}
|
||||
|
||||
fileExists(path: string): boolean {
|
||||
if (path.startsWith(this.#projectRoot)) {
|
||||
try {
|
||||
const st = fs.statSync(path);
|
||||
return !st.isDirectory();
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
const agentZipPath = this.#nativePathToAgentZipPath(path);
|
||||
if (agentZipPath === null) {
|
||||
return false;
|
||||
}
|
||||
return agentFiles.has(agentZipPath);
|
||||
}
|
||||
|
||||
directoryExists(path: string): boolean {
|
||||
if (path === compilerNodeModules) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (path.startsWith(this.#projectRoot)) {
|
||||
try {
|
||||
const st = fs.statSync(path);
|
||||
return st.isDirectory();
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
const agentZipPath = this.#nativePathToAgentZipPath(path);
|
||||
if (agentZipPath === null) {
|
||||
return false;
|
||||
}
|
||||
return agentDirectories.has(agentZipPath);
|
||||
}
|
||||
|
||||
createDirectory(path: string): void {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
getExecutingFilePath(): string {
|
||||
return [compilerRoot, "ext", "typescript.js"].join("/");
|
||||
}
|
||||
|
||||
getCurrentDirectory(): string {
|
||||
return this.#projectRoot;
|
||||
}
|
||||
|
||||
getDirectories(path: string): string[] {
|
||||
const result = new Set<string>();
|
||||
|
||||
if (path.startsWith(this.#projectRoot)) {
|
||||
try {
|
||||
for (const entry of fridaFs.list(path).filter(entry => entry.type === S_IFDIR)) {
|
||||
result.add(entry.name);
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
const agentZipPath = this.#nativePathToAgentZipPath(path);
|
||||
if (agentZipPath !== null) {
|
||||
for (const dir of agentDirectories) {
|
||||
const slashIndex = dir.lastIndexOf("/");
|
||||
const parent = dir.substring(0, slashIndex);
|
||||
if (parent === agentZipPath) {
|
||||
const basename = dir.substring(slashIndex + 1);
|
||||
result.add(basename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(result);
|
||||
}
|
||||
|
||||
readDirectory(path: string, extensions?: readonly string[], exclude?: readonly string[], include?: readonly string[], depth?: number): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
getModifiedTime(path: string): Date | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
setModifiedTime(path: string, time: Date): void {
|
||||
}
|
||||
|
||||
deleteFile(path: string): void {
|
||||
}
|
||||
|
||||
createHash(data: string): string {
|
||||
return this.createSHA256Hash(data);
|
||||
}
|
||||
|
||||
createSHA256Hash(data: string): string {
|
||||
return Checksum.compute("sha256", data);
|
||||
}
|
||||
|
||||
getMemoryUsage(): number {
|
||||
return Frida.heapSize;
|
||||
}
|
||||
|
||||
exit(exitCode?: number): void {
|
||||
}
|
||||
|
||||
realpath(path: string): string {
|
||||
return path;
|
||||
}
|
||||
|
||||
getEnvironmentVariable(name: string): string {
|
||||
return "";
|
||||
}
|
||||
|
||||
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any {
|
||||
return setTimeout(callback);
|
||||
}
|
||||
|
||||
clearTimeout(timeoutId: any): void {
|
||||
return clearTimeout(timeoutId);
|
||||
}
|
||||
|
||||
clearScreen(): void {
|
||||
}
|
||||
|
||||
base64decode(input: string): string {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
base64encode(input: string): string {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
#nativePathToAgentZipPath(path: string): string | null {
|
||||
const compilerPath = nativePathToCompilerPath(path);
|
||||
if (compilerPath !== null) {
|
||||
return compilerPath;
|
||||
}
|
||||
|
||||
if (path.startsWith(this.#projectNodeModules)) {
|
||||
return "/node_modules" + path.substring(this.#projectNodeModules.length);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function nativePathToCompilerPath(path: string): string | null {
|
||||
if (!path.startsWith(compilerRoot)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const subPath = path.substring(compilerRoot.length);
|
||||
if (subPath.startsWith("/node_modules")) {
|
||||
return subPath;
|
||||
}
|
||||
|
||||
return "/node_modules/frida-compile" + subPath;
|
||||
}
|
||||
|
||||
type WatcherId = number;
|
||||
type WatcherCallback = (eventKind: ts.FileWatcherEventKind) => void;
|
||||
type FileState = "pristine" | "modified" | "deleted";
|
||||
|
||||
interface WatchChangeMessage {
|
||||
id: WatcherId;
|
||||
state: FileState;
|
||||
}
|
||||
|
||||
function makeFileWatcher(path: string, type: "file" | "directory", pollingInterval: number, callback: WatcherCallback): ts.FileWatcher {
|
||||
if (nextWatcherId === 0xffffffff) {
|
||||
nextWatcherId = 1;
|
||||
}
|
||||
let id: WatcherId = nextWatcherId++;
|
||||
while (fileWatchers.has(id)) {
|
||||
id = nextWatcherId++;
|
||||
}
|
||||
|
||||
fileWatchers.set(id, callback);
|
||||
|
||||
send(["watch:add", id, path, type, pollingInterval ?? 0]);
|
||||
|
||||
return {
|
||||
close() {
|
||||
send(["watch:remove", id]);
|
||||
|
||||
fileWatchers.delete(id);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function makeDummyWatcher(): ts.FileWatcher {
|
||||
return {
|
||||
close() {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function fileWatcherEventKindFromFileState(state: FileState): ts.FileWatcherEventKind {
|
||||
switch (state) {
|
||||
case "pristine": return FileWatcherEventKind.Created;
|
||||
case "modified": return FileWatcherEventKind.Changed;
|
||||
case "deleted": return FileWatcherEventKind.Deleted;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
const { init, build, watch } = FridaCompilerAgentCore;
|
||||
|
||||
init();
|
||||
|
||||
rpc.exports = { build, watch };
|
||||
@@ -1,222 +0,0 @@
|
||||
const {
|
||||
compilerRoot,
|
||||
compilerNodeModules,
|
||||
agentDirectories,
|
||||
agentFiles,
|
||||
_build,
|
||||
_queryDefaultAssets,
|
||||
} = FridaCompilerAgentCore;
|
||||
|
||||
const projectRoot = '/agent';
|
||||
const projectNodeModules = '/agent/node_modules';
|
||||
const entrypoint = 'index.ts';
|
||||
const sourceMaps = 'included';
|
||||
const compression = 'none';
|
||||
|
||||
const hashes = new Map();
|
||||
let nextHashId = 1;
|
||||
|
||||
function main() {
|
||||
const system = new StubSystem();
|
||||
const assets = _queryDefaultAssets(projectRoot, system);
|
||||
_build({
|
||||
projectRoot,
|
||||
entrypoint,
|
||||
assets,
|
||||
system,
|
||||
sourceMaps,
|
||||
compression,
|
||||
onDiagnostic,
|
||||
});
|
||||
}
|
||||
|
||||
function onDiagnostic(diagnostic) {
|
||||
throwNotImplemented('diagnostic', ts.flattenDiagnosticMessageText(diagnostic, '\n'));
|
||||
}
|
||||
|
||||
class StubSystem {
|
||||
args = [];
|
||||
newLine = '\n';
|
||||
useCaseSensitiveFileNames = true;
|
||||
|
||||
write(s) {
|
||||
}
|
||||
|
||||
writeOutputIsTTY() {
|
||||
return true;
|
||||
}
|
||||
|
||||
readFile(path, encoding) {
|
||||
if (path === '/agent/tsconfig.json')
|
||||
return '{}';
|
||||
|
||||
if (path === '/agent/index.ts')
|
||||
return 'Interceptor.attach(ptr(1234), { onEnter(args) {} });'
|
||||
|
||||
const agentZipPath = this.#nativePathToAgentZipPath(path);
|
||||
if (agentZipPath !== null) {
|
||||
return agentFiles.get(agentZipPath);
|
||||
}
|
||||
|
||||
throwNotImplemented('readFile', path);
|
||||
}
|
||||
|
||||
getFileSize(path) {
|
||||
throwNotImplemented('getFileSize');
|
||||
}
|
||||
|
||||
writeFile(path, data, writeByteOrderMark) {
|
||||
throwNotImplemented('writeFile');
|
||||
}
|
||||
|
||||
watchFile(path, callback, pollingInterval, options) {
|
||||
throwNotImplemented('watchFile');
|
||||
}
|
||||
|
||||
watchDirectory(path, callback, recursive, options) {
|
||||
throwNotImplemented('watchDirectory');
|
||||
}
|
||||
|
||||
resolvePath(path) {
|
||||
throwNotImplemented('resolvePath');
|
||||
}
|
||||
|
||||
fileExists(path) {
|
||||
if (path === '/package.json' || path === '/agent/package.json')
|
||||
return false;
|
||||
|
||||
const agentZipPath = this.#nativePathToAgentZipPath(path);
|
||||
if (agentZipPath !== null)
|
||||
return agentFiles.has(agentZipPath);
|
||||
|
||||
throwNotImplemented('fileExists', path);
|
||||
}
|
||||
|
||||
directoryExists(path) {
|
||||
if (path === '/' || path === projectRoot || path === compilerNodeModules)
|
||||
return true;
|
||||
|
||||
const agentZipPath = this.#nativePathToAgentZipPath(path);
|
||||
if (agentZipPath !== null)
|
||||
return agentDirectories.has(agentZipPath);
|
||||
|
||||
if (path === '/node_modules/@types' || path === '/node_modules')
|
||||
return false;
|
||||
|
||||
throwNotImplemented('directoryExists', path);
|
||||
}
|
||||
|
||||
createDirectory(path) {
|
||||
throwNotImplemented('createDirectory');
|
||||
}
|
||||
|
||||
getExecutingFilePath() {
|
||||
return [compilerRoot, 'ext', 'typescript.js'].join('/');
|
||||
}
|
||||
|
||||
getCurrentDirectory() {
|
||||
return '/';
|
||||
}
|
||||
|
||||
getDirectories(path) {
|
||||
const agentZipPath = this.#nativePathToAgentZipPath(path);
|
||||
if (agentZipPath !== null) {
|
||||
const result = [];
|
||||
for (const dir of agentDirectories) {
|
||||
const slashIndex = dir.lastIndexOf('/');
|
||||
const parent = dir.substring(0, slashIndex);
|
||||
if (parent === agentZipPath) {
|
||||
const basename = dir.substring(slashIndex + 1);
|
||||
result.push(basename);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
throwNotImplemented('getDirectories', path);
|
||||
}
|
||||
|
||||
readDirectory(path, extensions, exclude, include, depth) {
|
||||
if (!extensions.includes('.ts'))
|
||||
throwNotImplemented('readDirectory', path, extensions);
|
||||
return ['index.ts'];
|
||||
}
|
||||
|
||||
getModifiedTime(path) {
|
||||
}
|
||||
|
||||
setModifiedTime(path, time) {
|
||||
}
|
||||
|
||||
deleteFile(path) {
|
||||
}
|
||||
|
||||
createHash(data) {
|
||||
let hash = hashes.get(data);
|
||||
if (hash === undefined) {
|
||||
hash = 'hash' + nextHashId++;
|
||||
hashes.set(data, hash);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
createSHA256Hash(data) {
|
||||
throwNotImplemented('createSHA256Hash');
|
||||
}
|
||||
|
||||
getMemoryUsage() {
|
||||
throwNotImplemented('getMemoryUsage');
|
||||
}
|
||||
|
||||
exit(exitCode) {
|
||||
}
|
||||
|
||||
realpath(path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
getEnvironmentVariable(name) {
|
||||
return '';
|
||||
}
|
||||
|
||||
setTimeout(callback, ms, ...args) {
|
||||
throwNotImplemented('setTimeout');
|
||||
}
|
||||
|
||||
clearTimeout(timeoutId) {
|
||||
throwNotImplemented('clearTimeout');
|
||||
}
|
||||
|
||||
clearScreen() {
|
||||
}
|
||||
|
||||
base64decode(input) {
|
||||
throwNotImplemented('base64decode');
|
||||
}
|
||||
|
||||
base64encode(input) {
|
||||
throwNotImplemented('base64encode');
|
||||
}
|
||||
|
||||
#nativePathToAgentZipPath(path) {
|
||||
if (path.startsWith(compilerRoot)) {
|
||||
const subPath = path.substring(compilerRoot.length);
|
||||
if (subPath.startsWith('/node_modules')) {
|
||||
return subPath;
|
||||
}
|
||||
return '/node_modules/frida-compile' + subPath;
|
||||
}
|
||||
|
||||
if (path.startsWith(projectNodeModules)) {
|
||||
return '/node_modules' + path.substring(projectNodeModules.length);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function throwNotImplemented(operation, ...details) {
|
||||
throw new Error('not implemented: ' + operation + ((details.length > 0) ? ` (${details.join(' ')})` : ''));
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifdef _MSC_VER
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#ifdef HAVE_ARM64
|
||||
# define FRIDA_CGO_INIT_FUNC _st0_arm64_windows_lib
|
||||
#elif GLIB_SIZEOF_VOID_P == 8
|
||||
# define FRIDA_CGO_INIT_FUNC _st0_amd64_windows_lib
|
||||
#else
|
||||
# define FRIDA_CGO_INIT_FUNC st0_386_windows_lib
|
||||
#endif
|
||||
|
||||
extern void FRIDA_CGO_INIT_FUNC ();
|
||||
|
||||
void
|
||||
_frida_compiler_backend_init_go_runtime (void)
|
||||
{
|
||||
FRIDA_CGO_INIT_FUNC ();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void
|
||||
_frida_compiler_backend_init_go_runtime (void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,816 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {
|
||||
FRIDA_OUTPUT_UNESCAPED,
|
||||
FRIDA_OUTPUT_HEX_BYTES,
|
||||
FRIDA_OUTPUT_C_STRING,
|
||||
} FridaOutputFormat;
|
||||
|
||||
typedef enum {
|
||||
FRIDA_BUNDLE_ESM,
|
||||
FRIDA_BUNDLE_IIFE,
|
||||
} FridaBundleFormat;
|
||||
|
||||
typedef void (* FridaBuildCompleteFunc) (char * bundle, char * error_message, void * user_data);
|
||||
typedef void (* FridaWatchReadyFunc) (uintptr_t session_handle, char * error_message, void * user_data);
|
||||
typedef void (* FridaStartingFunc) (void * user_data);
|
||||
typedef void (* FridaFinishedFunc) (void * user_data);
|
||||
typedef void (* FridaOutputFunc) (char * bundle, void * user_data);
|
||||
typedef void (* FridaDiagnosticFunc) (char * category, int code, char * path, int line, int character, char * text,
|
||||
void * user_data);
|
||||
typedef void (* FridaDestroyFunc) (void * user_data);
|
||||
|
||||
static inline void
|
||||
invoke_build_complete_func (FridaBuildCompleteFunc fn,
|
||||
char * bundle,
|
||||
char * error_message,
|
||||
void * user_data)
|
||||
{
|
||||
fn (bundle, error_message, user_data);
|
||||
}
|
||||
|
||||
static inline void
|
||||
invoke_watch_ready_func (FridaWatchReadyFunc fn,
|
||||
uintptr_t session_handle,
|
||||
char * error_message,
|
||||
void * user_data)
|
||||
{
|
||||
fn (session_handle, error_message, user_data);
|
||||
}
|
||||
|
||||
static inline void
|
||||
invoke_starting_func (FridaStartingFunc fn,
|
||||
void * user_data)
|
||||
{
|
||||
fn (user_data);
|
||||
}
|
||||
|
||||
static inline void
|
||||
invoke_finished_func (FridaFinishedFunc fn,
|
||||
void * user_data)
|
||||
{
|
||||
fn (user_data);
|
||||
}
|
||||
|
||||
static inline void
|
||||
invoke_output_func (FridaOutputFunc fn,
|
||||
char * bundle,
|
||||
void * user_data)
|
||||
{
|
||||
fn (bundle, user_data);
|
||||
}
|
||||
|
||||
static inline void
|
||||
invoke_diagnostic_func (FridaDiagnosticFunc fn,
|
||||
char * category,
|
||||
int code,
|
||||
char * path,
|
||||
int line,
|
||||
int character,
|
||||
char * text,
|
||||
void * user_data)
|
||||
{
|
||||
fn (category, code, path, line, character, text, user_data);
|
||||
}
|
||||
|
||||
static inline void
|
||||
invoke_destroy_func (FridaDestroyFunc fn,
|
||||
void * user_data)
|
||||
{
|
||||
fn (user_data);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime/cgo"
|
||||
"strings"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
esbuild "github.com/evanw/esbuild/pkg/api"
|
||||
tsscanner "github.com/frida/typescript-go/pkg/scanner"
|
||||
)
|
||||
|
||||
type BuildOptions struct {
|
||||
ProjectRoot string
|
||||
Entrypoint string
|
||||
OutputFormat OutputFormat
|
||||
BundleFormat BundleFormat
|
||||
DisableTypeCheck bool
|
||||
SourceMap bool
|
||||
Compress bool
|
||||
}
|
||||
|
||||
type OutputFormat C.FridaOutputFormat
|
||||
|
||||
const (
|
||||
OutputFormatUnescaped OutputFormat = OutputFormat(C.FRIDA_OUTPUT_UNESCAPED)
|
||||
OutputFormatHexBytes OutputFormat = OutputFormat(C.FRIDA_OUTPUT_HEX_BYTES)
|
||||
OutputFormatCString OutputFormat = OutputFormat(C.FRIDA_OUTPUT_C_STRING)
|
||||
)
|
||||
|
||||
type BundleFormat C.FridaBundleFormat
|
||||
|
||||
const (
|
||||
BundleFormatESM BundleFormat = BundleFormat(C.FRIDA_BUNDLE_ESM)
|
||||
BundleFormatIIFE BundleFormat = BundleFormat(C.FRIDA_BUNDLE_IIFE)
|
||||
)
|
||||
|
||||
type Diagnostic struct {
|
||||
category string
|
||||
code int
|
||||
path string
|
||||
line, character int
|
||||
text string
|
||||
}
|
||||
|
||||
type BuildEventCallbacks struct {
|
||||
OnStart BuildStartCallback
|
||||
OnEnd BuildEndCallback
|
||||
OnOutput BuildOutputCallback
|
||||
OnDiagnostic BuildDiagnosticCallback
|
||||
OnConfigChange ConfigChangeCallback
|
||||
}
|
||||
|
||||
type BuildStartCallback func()
|
||||
type BuildEndCallback func()
|
||||
type BuildOutputCallback func(bundle string)
|
||||
type BuildDiagnosticCallback func(d Diagnostic)
|
||||
|
||||
//export _frida_compiler_backend_build
|
||||
func _frida_compiler_backend_build(cProjectRoot, cEntrypoint *C.char, outputFormat C.FridaOutputFormat, bundleFormat C.FridaBundleFormat,
|
||||
disableTypeCheck, sourceMap, compress uintptr,
|
||||
onCompleteFn C.FridaBuildCompleteFunc, onCompleteData unsafe.Pointer, onCompleteDataDestroy C.FridaDestroyFunc,
|
||||
onDiagnosticFn C.FridaDiagnosticFunc, onDiagnosticData unsafe.Pointer) {
|
||||
options := BuildOptions{
|
||||
ProjectRoot: C.GoString(cProjectRoot),
|
||||
Entrypoint: C.GoString(cEntrypoint),
|
||||
OutputFormat: OutputFormat(outputFormat),
|
||||
BundleFormat: BundleFormat(bundleFormat),
|
||||
DisableTypeCheck: disableTypeCheck != 0,
|
||||
SourceMap: sourceMap != 0,
|
||||
Compress: compress != 0,
|
||||
}
|
||||
onComplete := NewCDelegate(onCompleteFn, onCompleteData, onCompleteDataDestroy)
|
||||
onDiagnostic := NewCDelegate(onDiagnosticFn, onDiagnosticData, nil)
|
||||
|
||||
go func() {
|
||||
defer onComplete.Dispose()
|
||||
defer onDiagnostic.Dispose()
|
||||
|
||||
onDiagnostic := makeCBuildDiagnosticCallback(onDiagnostic)
|
||||
|
||||
bundle, err := build(options, onDiagnostic)
|
||||
|
||||
var cBundle, cErrorMessage *C.char
|
||||
if err == nil {
|
||||
cBundle = C.CString(bundle)
|
||||
} else {
|
||||
cErrorMessage = C.CString(err.Error())
|
||||
}
|
||||
C.invoke_build_complete_func(onComplete.Func, cBundle, cErrorMessage, onComplete.Data)
|
||||
}()
|
||||
}
|
||||
|
||||
//export _frida_compiler_backend_watch
|
||||
func _frida_compiler_backend_watch(cProjectRoot, cEntrypoint *C.char, outputFormat C.FridaOutputFormat, bundleFormat C.FridaBundleFormat,
|
||||
disableTypeCheck, sourceMap, compress uintptr,
|
||||
onReadyFn C.FridaWatchReadyFunc, onReadyData unsafe.Pointer, onReadyDataDestroy C.FridaDestroyFunc,
|
||||
onStartingFn C.FridaStartingFunc, onStartingData unsafe.Pointer,
|
||||
onFinishedFn C.FridaFinishedFunc, onFinishedData unsafe.Pointer,
|
||||
onOutputFn C.FridaOutputFunc, onOutputData unsafe.Pointer,
|
||||
onDiagnosticFn C.FridaDiagnosticFunc, onDiagnosticData unsafe.Pointer) {
|
||||
options := BuildOptions{
|
||||
ProjectRoot: C.GoString(cProjectRoot),
|
||||
Entrypoint: C.GoString(cEntrypoint),
|
||||
OutputFormat: OutputFormat(outputFormat),
|
||||
BundleFormat: BundleFormat(bundleFormat),
|
||||
DisableTypeCheck: disableTypeCheck != 0,
|
||||
SourceMap: sourceMap != 0,
|
||||
Compress: compress != 0,
|
||||
}
|
||||
onReady := NewCDelegate(onReadyFn, onReadyData, onReadyDataDestroy)
|
||||
onStarting := NewCDelegate(onStartingFn, onStartingData, nil)
|
||||
onFinished := NewCDelegate(onFinishedFn, onFinishedData, nil)
|
||||
onOutput := NewCDelegate(onOutputFn, onOutputData, nil)
|
||||
onDiagnostic := NewCDelegate(onDiagnosticFn, onDiagnosticData, nil)
|
||||
|
||||
go func() {
|
||||
defer onReady.Dispose()
|
||||
|
||||
onDispose := func() {
|
||||
onStarting.Dispose()
|
||||
onFinished.Dispose()
|
||||
onOutput.Dispose()
|
||||
onDiagnostic.Dispose()
|
||||
}
|
||||
|
||||
callbacks := BuildEventCallbacks{
|
||||
OnStart: func() {
|
||||
C.invoke_starting_func(onStarting.Func, onStarting.Data)
|
||||
},
|
||||
OnEnd: func() {
|
||||
C.invoke_starting_func(onFinished.Func, onFinished.Data)
|
||||
},
|
||||
OnOutput: func(bundle string) {
|
||||
C.invoke_output_func(onOutput.Func, C.CString(bundle), onOutput.Data)
|
||||
},
|
||||
OnDiagnostic: makeCBuildDiagnosticCallback(onDiagnostic),
|
||||
}
|
||||
|
||||
session, err := NewWatchSession(options, onDispose, callbacks)
|
||||
|
||||
var cSession C.uintptr_t
|
||||
var cErrorMessage *C.char
|
||||
if err == nil {
|
||||
cSession = C.uintptr_t(cgo.NewHandle(session))
|
||||
} else {
|
||||
cErrorMessage = C.CString(err.Error())
|
||||
}
|
||||
C.invoke_watch_ready_func(onReady.Func, cSession, cErrorMessage, onReady.Data)
|
||||
}()
|
||||
}
|
||||
|
||||
//export _frida_compiler_backend_watch_session_dispose
|
||||
func _frida_compiler_backend_watch_session_dispose(h uintptr) {
|
||||
handle := cgo.Handle(h)
|
||||
session := handle.Value().(*WatchSession)
|
||||
session.Dispose()
|
||||
handle.Delete()
|
||||
}
|
||||
|
||||
func makeCBuildDiagnosticCallback(onDiagnostic *CDelegate[C.FridaDiagnosticFunc]) BuildDiagnosticCallback {
|
||||
return func(d Diagnostic) {
|
||||
var cPath *C.char
|
||||
if d.path != "" {
|
||||
cPath = C.CString(d.path)
|
||||
}
|
||||
|
||||
C.invoke_diagnostic_func(onDiagnostic.Func, C.CString(d.category), C.int(d.code), cPath, C.int(d.line),
|
||||
C.int(d.character), C.CString(d.text), onDiagnostic.Data)
|
||||
}
|
||||
}
|
||||
|
||||
func build(options BuildOptions, onDiagnostic BuildDiagnosticCallback) (bundle string, err error) {
|
||||
callbacks := BuildEventCallbacks{
|
||||
OnOutput: func(b string) {
|
||||
bundle = b
|
||||
},
|
||||
OnDiagnostic: onDiagnostic,
|
||||
}
|
||||
|
||||
ctx, err := makeContext(options, callbacks)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer ctx.Dispose()
|
||||
|
||||
result := ctx.Rebuild()
|
||||
|
||||
if len(result.Errors) != 0 {
|
||||
err = fmt.Errorf("Compilation failed")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type WatchSession struct {
|
||||
mu sync.Mutex
|
||||
options BuildOptions
|
||||
callbacks BuildEventCallbacks
|
||||
ctx esbuild.BuildContext
|
||||
onDispose SessionDisposeHandler
|
||||
}
|
||||
|
||||
type SessionDisposeHandler func()
|
||||
|
||||
func NewWatchSession(opts BuildOptions, onDispose SessionDisposeHandler, callbacks BuildEventCallbacks) (session *WatchSession, err error) {
|
||||
cbs := BuildEventCallbacks{
|
||||
OnStart: callbacks.OnStart,
|
||||
OnEnd: callbacks.OnEnd,
|
||||
OnOutput: callbacks.OnOutput,
|
||||
OnDiagnostic: callbacks.OnDiagnostic,
|
||||
OnConfigChange: func() {
|
||||
if callbacks.OnConfigChange != nil {
|
||||
callbacks.OnConfigChange()
|
||||
}
|
||||
session.onConfigChange()
|
||||
},
|
||||
}
|
||||
|
||||
ctx, err := makeContext(opts, cbs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Watch(esbuild.WatchOptions{})
|
||||
|
||||
session = &WatchSession{
|
||||
options: opts,
|
||||
callbacks: cbs,
|
||||
ctx: ctx,
|
||||
onDispose: onDispose,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *WatchSession) Dispose() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if s.ctx != nil {
|
||||
s.ctx.Dispose()
|
||||
s.ctx = nil
|
||||
}
|
||||
|
||||
s.onDispose()
|
||||
}
|
||||
|
||||
func (s *WatchSession) onConfigChange() {
|
||||
go func() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if s.ctx != nil {
|
||||
s.ctx.Dispose()
|
||||
s.ctx = nil
|
||||
}
|
||||
|
||||
ctx, err := makeContext(s.options, s.callbacks)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.ctx = ctx
|
||||
|
||||
ctx.Watch(esbuild.WatchOptions{})
|
||||
}()
|
||||
}
|
||||
|
||||
func makeContext(options BuildOptions, callbacks BuildEventCallbacks) (ctx esbuild.BuildContext, err error) {
|
||||
var e error
|
||||
|
||||
var projectRoot string
|
||||
if projectRoot, e = filepath.EvalSymlinks(options.ProjectRoot); e != nil {
|
||||
err = fmt.Errorf("Failed to resolve project root: %w", e)
|
||||
return
|
||||
}
|
||||
|
||||
var entrypoint string
|
||||
if filepath.IsAbs(options.Entrypoint) {
|
||||
entrypoint = options.Entrypoint
|
||||
} else {
|
||||
entrypoint = filepath.Join(projectRoot, options.Entrypoint)
|
||||
}
|
||||
if entrypoint, e = filepath.EvalSymlinks(entrypoint); e != nil {
|
||||
err = fmt.Errorf("Failed to resolve entrypoint: %w", e)
|
||||
return
|
||||
}
|
||||
rel, e := filepath.Rel(projectRoot, entrypoint)
|
||||
if e != nil {
|
||||
err = fmt.Errorf("Could not compute entrypoint path relative to project root: %w", e)
|
||||
return
|
||||
}
|
||||
if strings.HasPrefix(rel, "..") {
|
||||
err = fmt.Errorf("Entrypoint must be inside the project root")
|
||||
return
|
||||
}
|
||||
|
||||
isTS := strings.HasSuffix(entrypoint, ".ts")
|
||||
|
||||
var tsconfigCache *TSConfigCache
|
||||
var tsconfigText string
|
||||
var tsCompiler *TSCompiler
|
||||
|
||||
if isTS {
|
||||
tsconfigCache = NewTSConfigCache(projectRoot, options.SourceMap, callbacks.OnConfigChange)
|
||||
|
||||
tsCompiler = NewTSCompiler(projectRoot, entrypoint, tsconfigCache.GetCompilerOptions)
|
||||
|
||||
_, tsconfigText, err = tsconfigCache.GetCompilerOptions(tsCompiler)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Failed to load tsconfig options: %w", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
sourcemapOption := esbuild.SourceMapNone
|
||||
if options.SourceMap {
|
||||
if options.BundleFormat == BundleFormatESM {
|
||||
sourcemapOption = esbuild.SourceMapExternal
|
||||
} else {
|
||||
sourcemapOption = esbuild.SourceMapInline
|
||||
}
|
||||
}
|
||||
|
||||
minifyWhitespace := false
|
||||
minifyIdentifiers := false
|
||||
minifySyntax := false
|
||||
if options.Compress {
|
||||
minifyWhitespace = true
|
||||
minifyIdentifiers = true
|
||||
minifySyntax = true
|
||||
}
|
||||
|
||||
var format esbuild.Format
|
||||
if options.BundleFormat == BundleFormatESM {
|
||||
format = esbuild.FormatESModule
|
||||
} else {
|
||||
format = esbuild.FormatIIFE
|
||||
}
|
||||
|
||||
plugins := []esbuild.Plugin{
|
||||
makeBuildObserverPlugin(projectRoot, entrypoint, options, callbacks),
|
||||
}
|
||||
|
||||
if isTS && !options.DisableTypeCheck {
|
||||
plugins = append(plugins, makeTypeScriptPlugin(tsCompiler))
|
||||
}
|
||||
|
||||
plugins = append(plugins, makeFridaShimsPlugin())
|
||||
|
||||
buildOpts := esbuild.BuildOptions{
|
||||
Sourcemap: sourcemapOption,
|
||||
SourcesContent: esbuild.SourcesContentExclude,
|
||||
Target: esbuild.ES2022,
|
||||
MinifyWhitespace: minifyWhitespace,
|
||||
MinifyIdentifiers: minifyIdentifiers,
|
||||
MinifySyntax: minifySyntax,
|
||||
LegalComments: esbuild.LegalCommentsNone,
|
||||
Bundle: true,
|
||||
Outdir: projectRoot,
|
||||
AbsWorkingDir: projectRoot,
|
||||
Platform: esbuild.PlatformNode,
|
||||
Format: format,
|
||||
Inject: []string{"frida-builtins:///node-globals.js"},
|
||||
EntryPoints: []string{entrypoint},
|
||||
Write: false,
|
||||
Plugins: plugins,
|
||||
}
|
||||
|
||||
if isTS {
|
||||
buildOpts.TsconfigRaw = tsconfigText
|
||||
}
|
||||
|
||||
if buildCtx, ctxErr := esbuild.Context(buildOpts); ctxErr == nil {
|
||||
ctx = buildCtx
|
||||
} else {
|
||||
for _, e := range ctxErr.Errors {
|
||||
emitDiagnostic("error", e, callbacks.OnDiagnostic)
|
||||
}
|
||||
err = fmt.Errorf("Failed to create ESBuild context")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func emitDiagnostic(category string, message esbuild.Message, onDiagnostic BuildDiagnosticCallback) {
|
||||
d := Diagnostic{
|
||||
category: category,
|
||||
code: -1,
|
||||
text: message.Text,
|
||||
}
|
||||
|
||||
l := message.Location
|
||||
if l != nil {
|
||||
d.path = l.File
|
||||
d.line = l.Line
|
||||
d.character = l.Column
|
||||
}
|
||||
|
||||
if message.PluginName == "frida-custom-ts" && len(message.Notes) == 2 {
|
||||
d.category = message.Notes[0].Text
|
||||
fmt.Sscan(message.Notes[1].Text, &d.code)
|
||||
}
|
||||
|
||||
onDiagnostic(d)
|
||||
}
|
||||
|
||||
func changeToJS(p string) string {
|
||||
ext := path.Ext(p)
|
||||
base := p[:len(p)-len(ext)]
|
||||
return base + ".js"
|
||||
}
|
||||
|
||||
func makeBuildObserverPlugin(projectRoot, entrypoint string, options BuildOptions, callbacks BuildEventCallbacks) esbuild.Plugin {
|
||||
return esbuild.Plugin{
|
||||
Name: "frida-build-observer",
|
||||
Setup: func(build esbuild.PluginBuild) {
|
||||
if callbacks.OnStart != nil {
|
||||
build.OnStart(func() (esbuild.OnStartResult, error) {
|
||||
callbacks.OnStart()
|
||||
return esbuild.OnStartResult{}, nil
|
||||
})
|
||||
}
|
||||
|
||||
build.OnEnd(func(result *esbuild.BuildResult) (esbuild.OnEndResult, error) {
|
||||
if len(result.Errors) == 0 {
|
||||
var output string
|
||||
if options.BundleFormat == BundleFormatESM {
|
||||
output = makeESMBundle(result.OutputFiles, projectRoot, entrypoint)
|
||||
} else {
|
||||
output = string(result.OutputFiles[0].Contents)
|
||||
}
|
||||
|
||||
switch options.OutputFormat {
|
||||
case OutputFormatHexBytes:
|
||||
output = encodeStringToHexBytes(output)
|
||||
case OutputFormatCString:
|
||||
output = encodeStringToCString(output)
|
||||
}
|
||||
|
||||
callbacks.OnOutput(output)
|
||||
} else {
|
||||
for _, e := range result.Errors {
|
||||
emitDiagnostic("error", e, callbacks.OnDiagnostic)
|
||||
}
|
||||
for _, e := range result.Warnings {
|
||||
emitDiagnostic("warning", e, callbacks.OnDiagnostic)
|
||||
}
|
||||
}
|
||||
|
||||
if callbacks.OnEnd != nil {
|
||||
callbacks.OnEnd()
|
||||
}
|
||||
|
||||
return esbuild.OnEndResult{}, nil
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func makeESMBundle(files []esbuild.OutputFile, projectRoot, entrypoint string) string {
|
||||
entrypointJS, entrypointMap := outputForEntrypoint(projectRoot, entrypoint)
|
||||
relEntryp, err := filepath.Rel(projectRoot, entrypoint)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Cannot compute entrypoint relative path: %v", err))
|
||||
}
|
||||
entrySubDir := filepath.Dir(relEntryp)
|
||||
|
||||
entryIndexJS := -1
|
||||
entryIndexMap := -1
|
||||
for i, of := range files {
|
||||
if of.Path == entrypointJS {
|
||||
entryIndexJS = i
|
||||
}
|
||||
if of.Path == entrypointMap {
|
||||
entryIndexMap = i
|
||||
}
|
||||
}
|
||||
if entryIndexJS < 0 {
|
||||
panic(fmt.Sprintf("Entrypoint JS %q not found in files", entrypointJS))
|
||||
}
|
||||
|
||||
orderedFiles := make([]esbuild.OutputFile, 0, len(files))
|
||||
orderedFiles = append(orderedFiles, files[entryIndexJS])
|
||||
if entryIndexMap >= 0 {
|
||||
orderedFiles = append(orderedFiles, files[entryIndexMap])
|
||||
}
|
||||
|
||||
for i, of := range files {
|
||||
if i == entryIndexJS || i == entryIndexMap {
|
||||
continue
|
||||
}
|
||||
orderedFiles = append(orderedFiles, of)
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString("📦\n")
|
||||
for _, of := range orderedFiles {
|
||||
size := len(of.Contents)
|
||||
relPath, _ := filepath.Rel(projectRoot, of.Path)
|
||||
withSub := filepath.Join(entrySubDir, relPath)
|
||||
sb.WriteString(fmt.Sprintf("%d /%s\n", size, filepath.ToSlash(withSub)))
|
||||
}
|
||||
|
||||
sb.WriteString("✄\n")
|
||||
for i, of := range orderedFiles {
|
||||
sb.Write(of.Contents)
|
||||
if i < len(orderedFiles)-1 {
|
||||
sb.WriteString("\n✄\n")
|
||||
}
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func outputForEntrypoint(projectRoot, entrypoint string) (jsOut, mapOut string) {
|
||||
jsOut = filepath.Join(projectRoot, changeToJS(filepath.Base(entrypoint)))
|
||||
mapOut = jsOut + ".map"
|
||||
return
|
||||
}
|
||||
|
||||
func encodeStringToHexBytes(s string) string {
|
||||
n := len(s)
|
||||
if n == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
totalLen := n*4 + (n-1)*2 + 1
|
||||
buf := make([]byte, 0, totalLen)
|
||||
|
||||
hexDigits := "0123456789abcdef"
|
||||
for i := 0; i < n; i++ {
|
||||
b := s[i]
|
||||
buf = append(buf, '0', 'x')
|
||||
buf = append(buf,
|
||||
hexDigits[b>>4],
|
||||
hexDigits[b&0xF],
|
||||
)
|
||||
|
||||
if i < n-1 {
|
||||
if (i+1)%12 == 0 {
|
||||
buf = append(buf, ',', '\n')
|
||||
} else {
|
||||
buf = append(buf, ',', ' ')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf = append(buf, '\n')
|
||||
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
func encodeStringToCString(s string) string {
|
||||
n := len(s)
|
||||
if n == 0 {
|
||||
return `""`
|
||||
}
|
||||
|
||||
hexDigits := "0123456789abcdef"
|
||||
var bldr strings.Builder
|
||||
|
||||
openLiteral := func() {
|
||||
bldr.WriteByte('"')
|
||||
}
|
||||
closeLiteral := func() {
|
||||
bldr.WriteByte('"')
|
||||
}
|
||||
|
||||
inLiteral := false
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
c := s[i]
|
||||
|
||||
if !inLiteral {
|
||||
openLiteral()
|
||||
inLiteral = true
|
||||
}
|
||||
|
||||
if c == '\n' {
|
||||
bldr.WriteByte('\\')
|
||||
bldr.WriteByte('n')
|
||||
closeLiteral()
|
||||
inLiteral = false
|
||||
|
||||
if i < n-1 {
|
||||
bldr.WriteByte('\n')
|
||||
openLiteral()
|
||||
inLiteral = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
switch c {
|
||||
case '"':
|
||||
bldr.WriteByte('\\')
|
||||
bldr.WriteByte('"')
|
||||
case '\\':
|
||||
bldr.WriteByte('\\')
|
||||
bldr.WriteByte('\\')
|
||||
|
||||
case '\a':
|
||||
bldr.WriteByte('\\')
|
||||
bldr.WriteByte('a')
|
||||
case '\b':
|
||||
bldr.WriteByte('\\')
|
||||
bldr.WriteByte('b')
|
||||
case '\t':
|
||||
bldr.WriteByte('\\')
|
||||
bldr.WriteByte('t')
|
||||
case '\v':
|
||||
bldr.WriteByte('\\')
|
||||
bldr.WriteByte('v')
|
||||
case '\f':
|
||||
bldr.WriteByte('\\')
|
||||
bldr.WriteByte('f')
|
||||
case '\r':
|
||||
bldr.WriteByte('\\')
|
||||
bldr.WriteByte('r')
|
||||
|
||||
default:
|
||||
isPrintableExceptQuoteAndBackslash := c >= 0x20 && c <= 0x7E
|
||||
if isPrintableExceptQuoteAndBackslash {
|
||||
bldr.WriteByte(c)
|
||||
} else {
|
||||
bldr.WriteByte('\\')
|
||||
bldr.WriteByte('x')
|
||||
bldr.WriteByte(hexDigits[c>>4])
|
||||
bldr.WriteByte(hexDigits[c&0xF])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if inLiteral {
|
||||
closeLiteral()
|
||||
}
|
||||
|
||||
bldr.WriteByte('\n')
|
||||
|
||||
return bldr.String()
|
||||
}
|
||||
|
||||
func makeTypeScriptPlugin(compiler *TSCompiler) esbuild.Plugin {
|
||||
return esbuild.Plugin{
|
||||
Name: "frida-custom-ts",
|
||||
Setup: func(build esbuild.PluginBuild) {
|
||||
build.OnStart(func() (esbuild.OnStartResult, error) {
|
||||
compiler.EnsureProgramUpToDate()
|
||||
return esbuild.OnStartResult{}, nil
|
||||
})
|
||||
|
||||
build.OnEnd(func(result *esbuild.BuildResult) (esbuild.OnEndResult, error) {
|
||||
if len(result.Errors) == 0 {
|
||||
compiler.CommitPendingInputs()
|
||||
} else {
|
||||
compiler.DropPendingInputs()
|
||||
}
|
||||
return esbuild.OnEndResult{}, nil
|
||||
})
|
||||
|
||||
build.OnLoad(esbuild.OnLoadOptions{Filter: "\\.ts$"}, func(args esbuild.OnLoadArgs) (esbuild.OnLoadResult, error) {
|
||||
compiledJS, tsDiagnostics, err := compiler.Compile(args.Path)
|
||||
|
||||
result := esbuild.OnLoadResult{
|
||||
WatchFiles: compiler.WatchFiles(),
|
||||
WatchDirs: compiler.WatchDirs(),
|
||||
}
|
||||
|
||||
var esbuildMessages []esbuild.Message
|
||||
for _, d := range tsDiagnostics {
|
||||
f := d.File()
|
||||
pos := d.Pos()
|
||||
line, column := tsscanner.GetLineAndCharacterOfPosition(f, pos)
|
||||
|
||||
esbuildMessages = append(esbuildMessages, esbuild.Message{
|
||||
Text: d.Message(),
|
||||
Location: &esbuild.Location{
|
||||
File: f.FileName(),
|
||||
Line: line,
|
||||
Column: column,
|
||||
Length: d.Len(),
|
||||
LineText: f.Text()[pos:d.End()],
|
||||
},
|
||||
Notes: []esbuild.Note{
|
||||
{Text: d.Category().Name()},
|
||||
{Text: fmt.Sprintf("%d", d.Code())},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
mainErr := esbuild.Message{Text: err.Error()}
|
||||
result.Errors = append([]esbuild.Message{mainErr}, esbuildMessages...)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if len(esbuildMessages) > 0 {
|
||||
result.Errors = esbuildMessages
|
||||
return result, nil
|
||||
}
|
||||
|
||||
result.Contents = &compiledJS
|
||||
result.Loader = esbuild.LoaderJS
|
||||
return result, nil
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type CDelegate[F any] struct {
|
||||
Func F
|
||||
null F
|
||||
Data unsafe.Pointer
|
||||
dataDestroy C.FridaDestroyFunc
|
||||
}
|
||||
|
||||
func NewCDelegate[F any](function F, data unsafe.Pointer, dataDestroy C.FridaDestroyFunc) *CDelegate[F] {
|
||||
return &CDelegate[F]{Func: function, Data: data, dataDestroy: dataDestroy}
|
||||
}
|
||||
|
||||
func (d *CDelegate[F]) Dispose() {
|
||||
if d.dataDestroy != nil {
|
||||
C.invoke_destroy_func(d.dataDestroy, d.Data)
|
||||
}
|
||||
d.Func = d.null
|
||||
d.Data = nil
|
||||
d.dataDestroy = nil
|
||||
}
|
||||
|
||||
func main() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
global:
|
||||
_frida_compiler_backend_*;
|
||||
|
||||
local:
|
||||
*;
|
||||
};
|
||||
@@ -0,0 +1,375 @@
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
|
||||
def main(argv):
|
||||
go_config = json.loads(base64.b64decode(argv[1]).decode("utf-8"))
|
||||
output_dir, priv_dir, go, npm, *inputs = [Path(d).resolve() for d in argv[2:]]
|
||||
|
||||
try:
|
||||
build_backend(go_config, inputs, output_dir, priv_dir, go, npm)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(e, file=sys.stderr)
|
||||
print(
|
||||
"Output:\n\t| " + "\n\t| ".join(e.output.strip().split("\n")),
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(2)
|
||||
except Exception as e:
|
||||
print(e, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def build_backend(
|
||||
config: dict,
|
||||
inputs: List[Path],
|
||||
output_dir: Path,
|
||||
priv_dir: Path,
|
||||
go: Path,
|
||||
npm: Path,
|
||||
):
|
||||
base_dir = min(inputs, key=lambda p: len(p.parts)).parent
|
||||
for f in inputs:
|
||||
dest = priv_dir / f.relative_to(base_dir)
|
||||
dest.parent.mkdir(exist_ok=True)
|
||||
shutil.copy(f, dest)
|
||||
|
||||
go_sources = [str(f.relative_to(base_dir)) for f in inputs if f.suffix == ".go"]
|
||||
|
||||
def run(*args):
|
||||
return subprocess.run(
|
||||
args,
|
||||
cwd=priv_dir,
|
||||
env=config["env"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
encoding="utf-8",
|
||||
check=True,
|
||||
)
|
||||
|
||||
run(npm, "install")
|
||||
# run(npm, "link", "/home/oleavr/src/frida-fs")
|
||||
|
||||
mode = config["mode"]
|
||||
|
||||
backend_stem = "frida-compiler-backend"
|
||||
if mode == "c-shared":
|
||||
backend_stem += "-raw"
|
||||
|
||||
backend_a = priv_dir / f"{backend_stem}.a"
|
||||
backend_shlib = priv_dir / (f"{backend_stem}" + config["shlib_suffix"])
|
||||
backend_h = priv_dir / f"{backend_stem}.h"
|
||||
|
||||
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}")
|
||||
|
||||
run(
|
||||
go,
|
||||
"build",
|
||||
f"-buildmode={mode}",
|
||||
"-o",
|
||||
backend_a.name if mode == "c-archive" else backend_shlib.name,
|
||||
"-buildvcs=false",
|
||||
*extra_go_args,
|
||||
*go_sources,
|
||||
)
|
||||
|
||||
if mode == "c-archive":
|
||||
symbols_to_scramble = detect_conflictful_symbols_in_archive(backend_a, config["nm"])
|
||||
scramble_symbols_in_archive(backend_a, symbols_to_scramble, config["ranlib"])
|
||||
|
||||
if (mingw := config.get("mingw")) is not None and (abi := config["abi"]) in {
|
||||
"x86",
|
||||
"x86_64",
|
||||
}:
|
||||
prefix = Path(mingw["prefix"])
|
||||
ar = config["ar"]
|
||||
|
||||
libmingwex_a = prefix / "lib" / "libmingwex.a"
|
||||
if not libmingwex_a.exists():
|
||||
libmingwex_a = prefix / mingw["triplet"] / "lib" / "libmingwex.a"
|
||||
|
||||
libgcc_objects = [
|
||||
"_chkstk_ms.o",
|
||||
]
|
||||
run(*ar, "x", mingw["libcc"], *libgcc_objects)
|
||||
|
||||
gwex_objflavor = "64" if abi == "x86_64" else "32"
|
||||
gwex_objects = [
|
||||
f"lib{gwex_objflavor}_libmingwex_a-{name}.o"
|
||||
for name in [
|
||||
"dmisc",
|
||||
"gdtoa",
|
||||
"gmisc",
|
||||
"mingw_fprintf",
|
||||
"mingw_pformat",
|
||||
"misc",
|
||||
]
|
||||
]
|
||||
run(*ar, "x", libmingwex_a, *gwex_objects)
|
||||
|
||||
extra_objects = []
|
||||
if abi == "x86_64":
|
||||
run(*ar, "x", backend_a.name, "go.o")
|
||||
sort_pdata_in_object(Path(priv_dir) / "go.o")
|
||||
extra_objects.append("go.o")
|
||||
|
||||
run(*ar, "rs", backend_a.name, *libgcc_objects, *gwex_objects, *extra_objects)
|
||||
|
||||
shutil.copy(priv_dir / backend_a.name, output_dir / backend_a.name)
|
||||
else:
|
||||
shutil.copy(priv_dir / backend_shlib.name, output_dir / backend_shlib.name)
|
||||
|
||||
shutil.copy(priv_dir / backend_h.name, output_dir / backend_h.name)
|
||||
|
||||
|
||||
def detect_conflictful_symbols_in_archive(
|
||||
archive: Path, nm_cmd_array: List[str]
|
||||
) -> List[str]:
|
||||
result = []
|
||||
for line in subprocess.run(
|
||||
[*nm_cmd_array, str(archive)], capture_output=True, encoding="utf-8", check=True
|
||||
).stdout.splitlines():
|
||||
tokens = line.lstrip().split(" ")
|
||||
if len(tokens) >= 3 and tokens[1] in {"S", "T"}:
|
||||
symbol = tokens[2]
|
||||
if all(sub not in symbol for sub in ("/", ".", ":", "frida")):
|
||||
result.append(symbol)
|
||||
return result
|
||||
|
||||
|
||||
def scramble_symbols_in_archive(
|
||||
archive: Path, symbols: List[str], ranlib_cmd_array: List[str]
|
||||
):
|
||||
data = archive.read_bytes()
|
||||
|
||||
for old_sym in symbols:
|
||||
new_sym = roll_first_alpha(old_sym)
|
||||
data = data.replace(old_sym.encode("ascii"), new_sym.encode("ascii"))
|
||||
|
||||
archive.write_bytes(data)
|
||||
|
||||
subprocess.run([*ranlib_cmd_array, str(archive)], check=True)
|
||||
|
||||
|
||||
def roll_first_alpha(s: str) -> str:
|
||||
chars = list(s)
|
||||
for i, c in enumerate(chars):
|
||||
if c.isalpha():
|
||||
if c == "z":
|
||||
chars[i] = "a"
|
||||
elif c == "Z":
|
||||
chars[i] = "A"
|
||||
else:
|
||||
chars[i] = chr(ord(c) + 1)
|
||||
break
|
||||
return "".join(chars)
|
||||
|
||||
|
||||
# Work around the Go toolchain's almost-MSVC-compatible object files
|
||||
# until our patch is merged upstream:
|
||||
# https://go-review.googlesource.com/c/go/+/678795
|
||||
def sort_pdata_in_object(object_file: Path):
|
||||
data = bytearray(object_file.read_bytes())
|
||||
|
||||
# --- Parse COFF File Header (IMAGE_FILE_HEADER) ---
|
||||
# Format: WORD Machine; WORD NumberOfSections;
|
||||
# DWORD TimeDateStamp; DWORD PointerToSymbolTable;
|
||||
# DWORD NumberOfSymbols; WORD SizeOfOptionalHeader;
|
||||
# WORD Characteristics;
|
||||
IMAGE_FILE_HEADER_FMT = "<HHLLLHH"
|
||||
IMAGE_FILE_HEADER_SIZE = struct.calcsize(IMAGE_FILE_HEADER_FMT)
|
||||
|
||||
if len(data) < IMAGE_FILE_HEADER_SIZE:
|
||||
print("Error: File too small to be a valid COFF object", file=sys.stderr)
|
||||
return
|
||||
|
||||
(
|
||||
machine,
|
||||
number_of_sections,
|
||||
time_date_stamp,
|
||||
pointer_to_symbol_table,
|
||||
number_of_symbols,
|
||||
size_of_optional_header,
|
||||
characteristics,
|
||||
) = struct.unpack(IMAGE_FILE_HEADER_FMT, data[0:IMAGE_FILE_HEADER_SIZE])
|
||||
|
||||
# --- Parse Section Headers to find .pdata ---
|
||||
# Each IMAGE_SECTION_HEADER is 40 bytes: Name[8], Misc(VirtualSize), VirtualAddress,
|
||||
# SizeOfRawData, PointerToRawData, PointerToRelocations, PointerToLinenumbers,
|
||||
# NumberOfRelocations, NumberOfLinenumbers, Characteristics
|
||||
SECTION_HEADER_FMT = "<8sLLLLLLHHI"
|
||||
SECTION_HEADER_SIZE = struct.calcsize(SECTION_HEADER_FMT)
|
||||
section_table_offset = IMAGE_FILE_HEADER_SIZE + size_of_optional_header
|
||||
|
||||
pdata_section_index = None
|
||||
pdata_pointer_to_raw = None
|
||||
pdata_size_of_raw = None
|
||||
pdata_pointer_to_relocs = None
|
||||
pdata_number_of_relocs = None
|
||||
|
||||
# Keep all section headers for rewriting relocation entries later if needed
|
||||
section_headers = []
|
||||
|
||||
for i in range(number_of_sections):
|
||||
offset = section_table_offset + i * SECTION_HEADER_SIZE
|
||||
section_data = data[offset : offset + SECTION_HEADER_SIZE]
|
||||
(
|
||||
name,
|
||||
virtual_size,
|
||||
virtual_address,
|
||||
size_of_raw_data,
|
||||
pointer_to_raw_data,
|
||||
pointer_to_relocations,
|
||||
pointer_to_linenumbers,
|
||||
number_of_relocations,
|
||||
number_of_linenumbers,
|
||||
sec_characteristics,
|
||||
) = struct.unpack(SECTION_HEADER_FMT, section_data)
|
||||
|
||||
section_name = name.rstrip(b"\x00").decode("utf-8")
|
||||
section_headers.append(
|
||||
{
|
||||
"name": section_name,
|
||||
"offset": offset,
|
||||
"virtual_size": virtual_size,
|
||||
"virtual_address": virtual_address,
|
||||
"size_of_raw_data": size_of_raw_data,
|
||||
"pointer_to_raw_data": pointer_to_raw_data,
|
||||
"pointer_to_relocations": pointer_to_relocations,
|
||||
"number_of_relocations": number_of_relocations,
|
||||
}
|
||||
)
|
||||
|
||||
if section_name == ".pdata":
|
||||
pdata_section_index = i
|
||||
pdata_pointer_to_raw = pointer_to_raw_data
|
||||
pdata_size_of_raw = size_of_raw_data
|
||||
pdata_pointer_to_relocs = pointer_to_relocations
|
||||
pdata_number_of_relocs = number_of_relocations
|
||||
|
||||
if pdata_section_index is None:
|
||||
print("Error: No .pdata section found in the object file", file=sys.stderr)
|
||||
return
|
||||
|
||||
# --- Parse Symbol Table ---
|
||||
# Each IMAGE_SYMBOL is 18 bytes: Name[8], Value (4 bytes), SectionNumber (2 bytes),
|
||||
# Type (2 bytes), StorageClass (1 byte), NumberOfAuxSymbols (1 byte)
|
||||
SYMBOL_TABLE_ENTRY_SIZE = 18
|
||||
symbols = []
|
||||
for sym_index in range(number_of_symbols):
|
||||
sym_offset = pointer_to_symbol_table + sym_index * SYMBOL_TABLE_ENTRY_SIZE
|
||||
entry = data[sym_offset : sym_offset + SYMBOL_TABLE_ENTRY_SIZE]
|
||||
if len(entry) < SYMBOL_TABLE_ENTRY_SIZE:
|
||||
print("Error: Truncated symbol table entry", file=sys.stderr)
|
||||
return
|
||||
# Unpack: Name (8s), Value (L), SectionNumber (h), Type (H), StorageClass (B), NumberOfAuxSymbols (B)
|
||||
name_bytes, value, section_number, sym_type, storage_class, num_aux = (
|
||||
struct.unpack("<8sLhHBB", entry)
|
||||
)
|
||||
# Decode name: if name_bytes[0:4] are zeros, use string from offset in string table
|
||||
if name_bytes[:4] == b"\x00\x00\x00\x00":
|
||||
str_table_offset = struct.unpack("<L", name_bytes[4:])[0]
|
||||
# String table starts immediately after symbol table: size (4 bytes) + NUL-terminated strings
|
||||
str_tab_start = (
|
||||
pointer_to_symbol_table + number_of_symbols * SYMBOL_TABLE_ENTRY_SIZE
|
||||
)
|
||||
# Fetch the name from string table
|
||||
str_offset = str_tab_start + str_table_offset
|
||||
name_end = data.find(b"\x00", str_offset)
|
||||
symbol_name = data[str_offset:name_end].decode("utf-8")
|
||||
else:
|
||||
symbol_name = name_bytes.partition(b"\x00")[0].decode("utf-8")
|
||||
|
||||
symbols.append(
|
||||
{"name": symbol_name, "value": value, "section_number": section_number}
|
||||
)
|
||||
|
||||
# --- Read original .pdata section raw data ---
|
||||
pdata_data = data[pdata_pointer_to_raw : pdata_pointer_to_raw + pdata_size_of_raw]
|
||||
if len(pdata_data) % 12 != 0:
|
||||
print("Error: .pdata size is not a multiple of 12 bytes", file=sys.stderr)
|
||||
return
|
||||
num_entries = len(pdata_data) // 12
|
||||
|
||||
# --- Read relocation entries for .pdata ---
|
||||
# Each IMAGE_RELOCATION is 10 bytes: VirtualAddress (4), SymbolTableIndex (4), Type (2)
|
||||
RELOC_ENTRY_SIZE = 10
|
||||
reloc_entries = []
|
||||
reloc_table_start = pdata_pointer_to_relocs
|
||||
for rindex in range(pdata_number_of_relocs):
|
||||
reloff = reloc_table_start + rindex * RELOC_ENTRY_SIZE
|
||||
entry = data[reloff : reloff + RELOC_ENTRY_SIZE]
|
||||
if len(entry) < RELOC_ENTRY_SIZE:
|
||||
print("Error: Truncated relocation entry", file=sys.stderr)
|
||||
return
|
||||
va, sym_index, rtype = struct.unpack("<LLH", entry)
|
||||
reloc_entries.append(
|
||||
{"virtual_address": va, "symbol_index": sym_index, "type": rtype}
|
||||
)
|
||||
|
||||
# There should be 3 relocations per .pdata entry
|
||||
if len(reloc_entries) != num_entries * 3:
|
||||
print(
|
||||
"Warning: Expected {} relocations but found {}".format(
|
||||
num_entries * 3, len(reloc_entries)
|
||||
),
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
# --- Associate each entry with its relocations and sorting key ---
|
||||
entries = []
|
||||
for i in range(num_entries):
|
||||
entry_bytes = pdata_data[i * 12 : (i + 1) * 12]
|
||||
entry_relocs = reloc_entries[i * 3 : i * 3 + 3]
|
||||
|
||||
# Sorting key: use symbol.Value of first relocation"s symbol index
|
||||
sym_idx = entry_relocs[0]["symbol_index"]
|
||||
if sym_idx < 0 or sym_idx >= number_of_symbols:
|
||||
key = 0
|
||||
else:
|
||||
key = symbols[sym_idx]["value"]
|
||||
|
||||
entries.append({"entry_bytes": entry_bytes, "relocs": entry_relocs, "key": key})
|
||||
|
||||
# --- Sort entries by computed key ---
|
||||
entries.sort(key=lambda x: x["key"])
|
||||
|
||||
# --- Build new .pdata data buffer ---
|
||||
sorted_pdata = bytearray()
|
||||
for e in entries:
|
||||
sorted_pdata.extend(e["entry_bytes"])
|
||||
|
||||
# --- Build new relocation table buffer ---
|
||||
new_reloc_buf = bytearray()
|
||||
for new_i, e in enumerate(entries):
|
||||
for reloc in e["relocs"]:
|
||||
field_offset = reloc["virtual_address"] % 12
|
||||
new_va = new_i * 12 + field_offset
|
||||
packed = struct.pack("<LLH", new_va, reloc["symbol_index"], reloc["type"])
|
||||
new_reloc_buf.extend(packed)
|
||||
|
||||
# --- Write back sorted .pdata and new relocations into data buffer ---
|
||||
# Replace .pdata raw data
|
||||
data[pdata_pointer_to_raw : pdata_pointer_to_raw + pdata_size_of_raw] = sorted_pdata
|
||||
# Replace relocation entries
|
||||
data[pdata_pointer_to_relocs : pdata_pointer_to_relocs + len(new_reloc_buf)] = (
|
||||
new_reloc_buf
|
||||
)
|
||||
|
||||
# --- Emit the fixed object file ---
|
||||
object_file.write_bytes(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
@@ -1,3 +1,2 @@
|
||||
[resource-compiler]
|
||||
namespace = Frida.Data.Compiler
|
||||
compress = *.js:text:7; *.bin:generic:7
|
||||
@@ -1,3 +0,0 @@
|
||||
[resource-compiler]
|
||||
namespace = Frida.Data.Compiler
|
||||
compress = *.js:text:11; *.bin:generic:11
|
||||
+288
-338
@@ -5,59 +5,52 @@ namespace Frida {
|
||||
public signal void output (string bundle);
|
||||
public signal void diagnostics (Variant diagnostics);
|
||||
|
||||
public DeviceManager manager {
|
||||
get;
|
||||
construct;
|
||||
private size_t watch_session_handle = 0;
|
||||
private Gee.Queue<Diagnostic> pending_diagnostics = new Gee.ArrayQueue<Diagnostic> ();
|
||||
|
||||
private MainContext main_context;
|
||||
|
||||
// TODO: Remove the DeviceManager parameter.
|
||||
public Compiler (DeviceManager? manager = null) {
|
||||
Object ();
|
||||
}
|
||||
|
||||
#if HAVE_COMPILER_BACKEND
|
||||
private Promise<Agent>? load_request;
|
||||
private Gee.Map<uint, MonitorEntry> monitors = new Gee.HashMap<uint, MonitorEntry> ();
|
||||
private Gee.Set<MonitorEntry> dirty_monitors = new Gee.HashSet<MonitorEntry> ();
|
||||
private TimeoutSource? monitor_flush_timer;
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
#endif
|
||||
|
||||
public Compiler (DeviceManager manager) {
|
||||
Object (manager: manager);
|
||||
static construct {
|
||||
CompilerBackend.init ();
|
||||
}
|
||||
|
||||
#if HAVE_COMPILER_BACKEND
|
||||
public override void dispose () {
|
||||
if (load_request != null)
|
||||
close.begin ();
|
||||
|
||||
base.dispose ();
|
||||
construct {
|
||||
main_context = Frida.get_main_context ();
|
||||
}
|
||||
|
||||
private async void close () {
|
||||
try {
|
||||
var agent = yield get_agent (null);
|
||||
yield agent.close (null);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
load_request = null;
|
||||
|
||||
if (monitor_flush_timer != null) {
|
||||
monitor_flush_timer.destroy ();
|
||||
monitor_flush_timer = null;
|
||||
}
|
||||
|
||||
foreach (var entry in monitors.values)
|
||||
detach_monitor_entry (entry);
|
||||
monitors.clear ();
|
||||
~Compiler () {
|
||||
cancel_watch ();
|
||||
}
|
||||
#endif
|
||||
|
||||
public async string build (string entrypoint, BuildOptions? options = null, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
#if HAVE_COMPILER_BACKEND
|
||||
var agent = yield get_agent (cancellable);
|
||||
CompilerBackend.check_available ();
|
||||
|
||||
BuildOptions opts = (options != null) ? options : new BuildOptions ();
|
||||
string project_root = compute_project_root (entrypoint, opts);
|
||||
|
||||
starting ();
|
||||
try {
|
||||
string bundle = yield agent.build (entrypoint, options, cancellable);
|
||||
string? bundle = null;
|
||||
string? error_message = null;
|
||||
CompilerBackend.BuildCompleteFunc on_complete = (b, e) => {
|
||||
bundle = b;
|
||||
error_message = e;
|
||||
schedule_on_frida_thread (build.callback);
|
||||
};
|
||||
|
||||
CompilerBackend.build (project_root, entrypoint, opts.output_format, opts.bundle_format,
|
||||
(size_t) (opts.type_check == NONE), (size_t) (opts.source_maps == INCLUDED),
|
||||
(size_t) (opts.compression == TERSER), (owned) on_complete, on_diagnostic);
|
||||
yield;
|
||||
|
||||
if (error_message != null)
|
||||
throw new Error.INVALID_ARGUMENT ("%s", error_message);
|
||||
|
||||
output (bundle);
|
||||
|
||||
@@ -65,9 +58,6 @@ namespace Frida {
|
||||
} finally {
|
||||
finished ();
|
||||
}
|
||||
#else
|
||||
throw_not_supported ();
|
||||
#endif
|
||||
}
|
||||
|
||||
public string build_sync (string entrypoint, BuildOptions? options = null, Cancellable? cancellable = null)
|
||||
@@ -89,13 +79,38 @@ namespace Frida {
|
||||
|
||||
public async void watch (string entrypoint, WatchOptions? options = null, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
#if HAVE_COMPILER_BACKEND
|
||||
var agent = yield get_agent (cancellable);
|
||||
CompilerBackend.check_available ();
|
||||
|
||||
yield agent.watch (entrypoint, options, cancellable);
|
||||
#else
|
||||
throw_not_supported ();
|
||||
#endif
|
||||
WatchOptions opts = (options != null) ? options : new WatchOptions ();
|
||||
string project_root = compute_project_root (entrypoint, opts);
|
||||
|
||||
cancel_watch ();
|
||||
|
||||
size_t session_handle = 0;
|
||||
string? error_message = null;
|
||||
CompilerBackend.WatchReadyFunc on_ready = (h, e) => {
|
||||
session_handle = h;
|
||||
error_message = e;
|
||||
schedule_on_frida_thread (watch.callback);
|
||||
};
|
||||
|
||||
CompilerBackend.watch (project_root, entrypoint, opts.output_format, opts.bundle_format,
|
||||
(size_t) (opts.type_check == NONE), (size_t) (opts.source_maps == INCLUDED),
|
||||
(size_t) (opts.compression == TERSER), (owned) on_ready, on_starting, on_finished,
|
||||
on_output, on_diagnostic);
|
||||
yield;
|
||||
|
||||
if (error_message != null)
|
||||
throw new Error.INVALID_ARGUMENT ("%s", error_message);
|
||||
|
||||
watch_session_handle = session_handle;
|
||||
}
|
||||
|
||||
private void cancel_watch () {
|
||||
if (watch_session_handle != 0) {
|
||||
CompilerBackend.WatchSession.dispose (watch_session_handle);
|
||||
watch_session_handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void watch_sync (string entrypoint, WatchOptions? options = null, Cancellable? cancellable = null)
|
||||
@@ -115,343 +130,238 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_COMPILER_BACKEND
|
||||
private void on_watch_add (uint id, string path, string type, int64 polling_interval) {
|
||||
var file = File.parse_name (path);
|
||||
private void on_starting () {
|
||||
schedule_on_frida_thread (() => {
|
||||
starting ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
GLib.FileMonitor monitor;
|
||||
try {
|
||||
if (type == "file")
|
||||
monitor = file.monitor_file (FileMonitorFlags.SEND_MOVED, io_cancellable);
|
||||
else if (type == "directory")
|
||||
monitor = file.monitor_directory (FileMonitorFlags.NONE, io_cancellable);
|
||||
else
|
||||
assert_not_reached ();
|
||||
} catch (GLib.Error e) {
|
||||
return;
|
||||
private void on_finished () {
|
||||
schedule_on_frida_thread (() => {
|
||||
finished ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
private void on_output (owned string bundle) {
|
||||
schedule_on_frida_thread (() => {
|
||||
output (bundle);
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
private void on_diagnostic (owned string category, int code, owned string? path, int line, int character,
|
||||
owned string text) {
|
||||
var diag = new Diagnostic () {
|
||||
category = category,
|
||||
code = code,
|
||||
path = path,
|
||||
line = line,
|
||||
character = character,
|
||||
text = text,
|
||||
};
|
||||
|
||||
bool schedule_emit = false;
|
||||
lock (pending_diagnostics) {
|
||||
schedule_emit = pending_diagnostics.is_empty;
|
||||
pending_diagnostics.add (diag);
|
||||
}
|
||||
|
||||
if (polling_interval != 0)
|
||||
monitor.rate_limit = (int) polling_interval;
|
||||
|
||||
var entry = new MonitorEntry (id, monitor);
|
||||
entry.file_changed.connect (on_file_changed);
|
||||
monitors[id] = entry;
|
||||
}
|
||||
|
||||
private void on_watch_remove (uint id) {
|
||||
MonitorEntry? entry;
|
||||
if (monitors.unset (id, out entry))
|
||||
detach_monitor_entry (entry);
|
||||
}
|
||||
|
||||
private void detach_monitor_entry (MonitorEntry entry) {
|
||||
dirty_monitors.remove (entry);
|
||||
|
||||
entry.file_changed.disconnect (on_file_changed);
|
||||
entry.destroy ();
|
||||
}
|
||||
|
||||
private void on_file_changed (MonitorEntry entry) {
|
||||
dirty_monitors.add (entry);
|
||||
|
||||
if (monitor_flush_timer == null) {
|
||||
var source = new TimeoutSource (50);
|
||||
source.set_callback (on_monitor_flush_tick);
|
||||
source.attach (MainContext.get_thread_default ());
|
||||
monitor_flush_timer = source;
|
||||
if (schedule_emit) {
|
||||
schedule_on_frida_thread (() => {
|
||||
emit_pending_diagnostics ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private bool on_monitor_flush_tick () {
|
||||
var settled = new Gee.ArrayList<MonitorEntry> ();
|
||||
var now = get_monotonic_time ();
|
||||
settled.add_all_iterator (dirty_monitors.filter (m => now - m.last_change >= 50000));
|
||||
|
||||
Agent agent = load_request.future.value;
|
||||
foreach (var entry in settled) {
|
||||
agent.notify_file_changed (entry.id, entry.state, io_cancellable);
|
||||
dirty_monitors.remove (entry);
|
||||
private void emit_pending_diagnostics () {
|
||||
var batch = new Gee.ArrayList<Diagnostic> ();
|
||||
lock (pending_diagnostics) {
|
||||
batch.add_all (pending_diagnostics);
|
||||
pending_diagnostics.clear ();
|
||||
}
|
||||
|
||||
if (!dirty_monitors.is_empty)
|
||||
return Source.CONTINUE;
|
||||
var builder = new VariantBuilder (new VariantType.array (VariantType.VARDICT));
|
||||
|
||||
monitor_flush_timer = null;
|
||||
return Source.REMOVE;
|
||||
}
|
||||
|
||||
private async Agent get_agent (Cancellable? cancellable) throws Error, IOError {
|
||||
Future<Agent> future;
|
||||
if (load_request != null) {
|
||||
future = load_request.future;
|
||||
} else {
|
||||
load_request = new Promise<Agent> ();
|
||||
future = load_request.future;
|
||||
|
||||
load_agent.begin ();
|
||||
foreach (var d in batch) {
|
||||
builder.open (VariantType.VARDICT);
|
||||
builder.add ("{sv}", "category", new Variant.string (d.category));
|
||||
builder.add ("{sv}", "code", new Variant.int64 (d.code));
|
||||
if (d.path != null) {
|
||||
var b = new VariantBuilder (VariantType.VARDICT);
|
||||
b.add ("{sv}", "path", new Variant.string (d.path));
|
||||
b.add ("{sv}", "line", new Variant.int64 (d.line));
|
||||
b.add ("{sv}", "character", new Variant.int64 (d.character));
|
||||
builder.add ("{sv}", "file", b.end ());
|
||||
}
|
||||
builder.add ("{sv}", "text", new Variant.string (d.text));
|
||||
builder.close ();
|
||||
}
|
||||
|
||||
return yield future.wait_async (cancellable);
|
||||
diagnostics (builder.end ());
|
||||
}
|
||||
|
||||
private async void load_agent () {
|
||||
try {
|
||||
var device = yield manager.get_device_by_type (LOCAL, 0, io_cancellable);
|
||||
var session = yield device.get_host_session (io_cancellable);
|
||||
var agent = new Agent (this, (LocalHostSession) session);
|
||||
|
||||
load_request.resolve (agent);
|
||||
} catch (GLib.Error e) {
|
||||
if ((e is Error || e is IOError.CANCELLED))
|
||||
e = new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
|
||||
load_request.reject (e);
|
||||
load_request = null;
|
||||
}
|
||||
}
|
||||
#else
|
||||
[NoReturn]
|
||||
private void throw_not_supported () throws Error {
|
||||
throw new Error.NOT_SUPPORTED ("Compiler backend disabled at build-time");
|
||||
}
|
||||
#endif
|
||||
|
||||
private T create<T> () {
|
||||
return Object.new (typeof (T), parent: this);
|
||||
}
|
||||
|
||||
protected void schedule_on_frida_thread (owned SourceFunc function) {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback ((owned) function);
|
||||
source.attach (main_context);
|
||||
}
|
||||
|
||||
private class Diagnostic {
|
||||
public string category;
|
||||
public int code;
|
||||
public string? path;
|
||||
public int line;
|
||||
public int character;
|
||||
public string text;
|
||||
}
|
||||
|
||||
private abstract class CompilerTask<T> : AsyncTask<T> {
|
||||
public weak Compiler parent {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace CompilerBackend {
|
||||
private void init () {
|
||||
#if HAVE_COMPILER_BACKEND
|
||||
private class Agent : InternalAgent {
|
||||
public weak Compiler parent {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private Promise<string> source_request = new Promise<string> ();
|
||||
private Promise<Bytes?> snapshot_request = new Promise<Bytes?> ();
|
||||
|
||||
public Agent (Compiler parent, LocalHostSession host_session) {
|
||||
Object (
|
||||
parent: parent,
|
||||
host_session: host_session,
|
||||
#if HAVE_V8
|
||||
script_runtime: ScriptRuntime.V8
|
||||
#if COMPILER_BACKEND_STATIC_COMPILATION
|
||||
_init_go_runtime ();
|
||||
build = (BuildFunc) _build;
|
||||
watch = (WatchFunc) _watch;
|
||||
WatchSession.dispose = (WatchSession.DisposeFunc) WatchSession._dispose;
|
||||
#else
|
||||
script_runtime: ScriptRuntime.DEFAULT
|
||||
#endif
|
||||
);
|
||||
}
|
||||
unowned uint8[] backend_so = Frida.Data.Compiler.get_frida_compiler_backend_so_blob ().data;
|
||||
|
||||
construct {
|
||||
load_resources ();
|
||||
}
|
||||
FileDescriptor fd;
|
||||
if (MemoryFileDescriptor.is_supported ()) {
|
||||
fd = MemoryFileDescriptor.from_bytes ("frida-compiler-backend.so", new Bytes.static (backend_so));
|
||||
} else {
|
||||
string name_used;
|
||||
try {
|
||||
fd = new FileDescriptor (FileUtils.open_tmp ("frida-compiler-backend-XXXXXX.so", out name_used));
|
||||
FileUtils.unlink (name_used);
|
||||
|
||||
private void load_resources () {
|
||||
new Thread<void> ("compiler-agent-loader", () => {
|
||||
var agent_js_blob = Data.Compiler.get_agent_js_blob ();
|
||||
Bytes agent_js_bytes = decompress (agent_js_blob.data, agent_js_blob.uncompressed_size);
|
||||
source_request.resolve ((string) agent_js_bytes.get_data ());
|
||||
var output = new UnixOutputStream (fd.handle, false);
|
||||
output.write_all (backend_so, null);
|
||||
|
||||
var snapshot_blob = Data.Compiler.get_snapshot_bin_blob ();
|
||||
Bytes snapshot = decompress (snapshot_blob.data, snapshot_blob.uncompressed_size);
|
||||
snapshot_request.resolve ((snapshot.get_size () != 0) ? snapshot : null);
|
||||
});
|
||||
}
|
||||
|
||||
public async string build (string entrypoint, BuildOptions? options, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
BuildOptions opts = (options != null) ? options : new BuildOptions ();
|
||||
|
||||
Json.Node bundle = yield call ("build", new Json.Node[] {
|
||||
new Json.Node.alloc ().init_string (compute_project_root (entrypoint, opts)),
|
||||
new Json.Node.alloc ().init_string (entrypoint),
|
||||
new Json.Node.alloc ().init_string (opts.source_maps.to_nick ()),
|
||||
new Json.Node.alloc ().init_string (opts.compression.to_nick ()),
|
||||
}, null, cancellable);
|
||||
return bundle.get_string ();
|
||||
}
|
||||
|
||||
public async void watch (string entrypoint, WatchOptions? options, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
WatchOptions opts = (options != null) ? options : new WatchOptions ();
|
||||
|
||||
yield call ("watch", new Json.Node[] {
|
||||
new Json.Node.alloc ().init_string (compute_project_root (entrypoint, opts)),
|
||||
new Json.Node.alloc ().init_string (entrypoint),
|
||||
new Json.Node.alloc ().init_string (opts.source_maps.to_nick ()),
|
||||
new Json.Node.alloc ().init_string (opts.compression.to_nick ()),
|
||||
}, null, cancellable);
|
||||
}
|
||||
|
||||
private static string compute_project_root (string entrypoint, CompilerOptions options) {
|
||||
string? project_root = options.project_root;
|
||||
|
||||
if (project_root != null)
|
||||
return project_root;
|
||||
|
||||
if (Path.is_absolute (entrypoint))
|
||||
return Path.get_dirname (entrypoint);
|
||||
|
||||
return Environment.get_current_dir ();
|
||||
}
|
||||
|
||||
public void notify_file_changed (uint id, FileState state, Cancellable? cancellable) {
|
||||
var notification = new Json.Builder ();
|
||||
notification
|
||||
.begin_object ()
|
||||
.set_member_name ("type")
|
||||
.add_string_value ("watch:change")
|
||||
.set_member_name ("id")
|
||||
.add_int_value (id)
|
||||
.set_member_name ("state")
|
||||
.add_string_value (Marshal.enum_to_nick<FileState> (state))
|
||||
.end_object ();
|
||||
post.begin (notification.get_root (), cancellable);
|
||||
}
|
||||
|
||||
protected override async uint get_target_pid (Cancellable? cancellable) throws Error, IOError {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected override async string? load_source (Cancellable? cancellable) throws Error, IOError {
|
||||
return yield source_request.future.wait_async (cancellable);
|
||||
}
|
||||
|
||||
protected override async Bytes? load_snapshot (Cancellable? cancellable, out SnapshotTransport transport)
|
||||
throws Error, IOError {
|
||||
transport = SHARED_MEMORY;
|
||||
return yield snapshot_request.future.wait_async (cancellable);
|
||||
}
|
||||
|
||||
protected override void on_event (string type, Json.Array event) {
|
||||
if (type == "diagnostics") {
|
||||
on_build_diagnostics (event.get_array_element (1));
|
||||
} else if (type == "watch:compilation-starting") {
|
||||
parent.starting ();
|
||||
} else if (type == "watch:compilation-finished") {
|
||||
parent.finished ();
|
||||
} else if (type == "watch:bundle-updated") {
|
||||
on_watch_bundle_updated (event.get_string_element (1));
|
||||
} else if (type == "watch:add") {
|
||||
parent.on_watch_add ((uint) event.get_int_element (1), event.get_string_element (2),
|
||||
event.get_string_element (3), event.get_int_element (4));
|
||||
} else if (type == "watch:remove") {
|
||||
parent.on_watch_remove ((uint) event.get_int_element (1));
|
||||
} else {
|
||||
Posix.lseek (fd.handle, 0, Posix.SEEK_SET);
|
||||
} catch (GLib.Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
private void on_build_diagnostics (Json.Array diagnostics) {
|
||||
uint n = diagnostics.get_length ();
|
||||
|
||||
var builder = new VariantBuilder (new VariantType.array (VariantType.VARDICT));
|
||||
for (uint i = 0; i != n; i++) {
|
||||
var diag = diagnostics.get_array_element (i);
|
||||
builder.open (VariantType.VARDICT);
|
||||
builder.add ("{sv}", "category", new Variant.string (diag.get_string_element (0)));
|
||||
builder.add ("{sv}", "code", new Variant.int64 (diag.get_int_element (1)));
|
||||
if (!diag.get_null_element (2)) {
|
||||
var file = diag.get_array_element (2);
|
||||
var b = new VariantBuilder (VariantType.VARDICT);
|
||||
b.add ("{sv}", "path", new Variant.string (file.get_string_element (0)));
|
||||
b.add ("{sv}", "line", new Variant.int64 (file.get_int_element (1)));
|
||||
b.add ("{sv}", "character", new Variant.int64 (file.get_int_element (2)));
|
||||
builder.add ("{sv}", "file", b.end ());
|
||||
}
|
||||
builder.add ("{sv}", "text", new Variant.string (diag.get_string_element (3)));
|
||||
builder.close ();
|
||||
}
|
||||
|
||||
parent.diagnostics (builder.end ());
|
||||
}
|
||||
|
||||
private void on_watch_bundle_updated (string bundle) {
|
||||
parent.output (bundle);
|
||||
}
|
||||
|
||||
private static Bytes decompress (uint8[] data, uint uncompressed_size) {
|
||||
var decoder = new Brotli.Decoder ();
|
||||
decoder.set_parameter (LARGE_WINDOW, 1);
|
||||
|
||||
uint8[] uncompressed_data = new uint8[uncompressed_size + 1];
|
||||
size_t available_in = data.length;
|
||||
size_t available_out = uncompressed_size;
|
||||
uint8 * next_in = data;
|
||||
uint8 * next_out = uncompressed_data;
|
||||
decoder.decompress_stream (&available_in, &next_in, &available_out, &next_out);
|
||||
uncompressed_data.length = (int) (next_out - (uint8 *) uncompressed_data);
|
||||
|
||||
return new Bytes.take ((owned) uncompressed_data);
|
||||
Module backend;
|
||||
try {
|
||||
backend = new Module ("/proc/self/fd/%d".printf (fd.handle), LOCAL);
|
||||
} catch (ModuleError e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
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");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
private class MonitorEntry : Object {
|
||||
public signal void file_changed ();
|
||||
|
||||
public uint id;
|
||||
public GLib.FileMonitor monitor;
|
||||
|
||||
public FileState state = PRISTINE;
|
||||
public int64 last_change = -1;
|
||||
|
||||
public MonitorEntry (uint id, GLib.FileMonitor monitor) {
|
||||
this.id = id;
|
||||
this.monitor = monitor;
|
||||
|
||||
monitor.changed.connect (on_file_changed);
|
||||
}
|
||||
|
||||
public void destroy () {
|
||||
monitor.changed.disconnect (on_file_changed);
|
||||
monitor.cancel ();
|
||||
}
|
||||
|
||||
private void on_file_changed (File file, File? other_file, FileMonitorEvent event) {
|
||||
switch (event) {
|
||||
case CREATED:
|
||||
state = PRISTINE;
|
||||
break;
|
||||
case CHANGED:
|
||||
if (state != PRISTINE)
|
||||
state = MODIFIED;
|
||||
break;
|
||||
case DELETED:
|
||||
case MOVED:
|
||||
state = DELETED;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
last_change = get_monotonic_time ();
|
||||
|
||||
file_changed ();
|
||||
}
|
||||
private void check_available () throws Error {
|
||||
if (build == null)
|
||||
throw new Error.NOT_SUPPORTED ("Compiler backend disabled at build-time");
|
||||
}
|
||||
|
||||
private enum FileState {
|
||||
PRISTINE,
|
||||
MODIFIED,
|
||||
DELETED,
|
||||
private BuildFunc? build;
|
||||
private WatchFunc? watch;
|
||||
|
||||
[CCode (has_target = false)]
|
||||
private delegate void BuildFunc (string project_root, string entrypoint, OutputFormat output_format,
|
||||
BundleFormat bundle_format, size_t disable_type_check, size_t source_map, size_t compress,
|
||||
owned BuildCompleteFunc on_complete, DiagnosticFunc on_diagnostic);
|
||||
|
||||
[CCode (has_target = false)]
|
||||
private delegate void WatchFunc (string project_root, string entrypoint, OutputFormat output_format,
|
||||
BundleFormat bundle_format, size_t disable_type_check, size_t source_map, size_t compress,
|
||||
owned WatchReadyFunc on_ready, StartingFunc on_starting, FinishedFunc on_finished, OutputFunc on_output,
|
||||
DiagnosticFunc on_diagnostic);
|
||||
|
||||
#if COMPILER_BACKEND_STATIC_COMPILATION
|
||||
private extern void _init_go_runtime ();
|
||||
private extern void _build ();
|
||||
private extern void _watch ();
|
||||
#endif
|
||||
|
||||
namespace WatchSession {
|
||||
[CCode (has_target = false)]
|
||||
private delegate void DisposeFunc (size_t handle);
|
||||
|
||||
private DisposeFunc? dispose;
|
||||
|
||||
#if COMPILER_BACKEND_STATIC_COMPILATION
|
||||
private extern void _dispose ();
|
||||
#endif
|
||||
}
|
||||
|
||||
private delegate void BuildCompleteFunc (owned string? bundle, owned string? error_message);
|
||||
private delegate void WatchReadyFunc (size_t session_handle, owned string? error_message);
|
||||
private delegate void StartingFunc ();
|
||||
private delegate void FinishedFunc ();
|
||||
private delegate void OutputFunc (owned string bundle);
|
||||
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_STATIC_COMPILATION
|
||||
private static T resolve_symbol<T> (Module m, string name) {
|
||||
void * address;
|
||||
if (!m.symbol (name, out address))
|
||||
assert_not_reached ();
|
||||
return (T) address;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private string compute_project_root (string entrypoint, CompilerOptions options) {
|
||||
string? project_root = options.project_root;
|
||||
|
||||
if (project_root != null)
|
||||
return project_root;
|
||||
|
||||
if (Path.is_absolute (entrypoint))
|
||||
return Path.get_dirname (entrypoint);
|
||||
|
||||
return Environment.get_current_dir ();
|
||||
}
|
||||
|
||||
public class CompilerOptions : Object {
|
||||
public string? project_root {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public OutputFormat output_format {
|
||||
get;
|
||||
set;
|
||||
default = UNESCAPED;
|
||||
}
|
||||
|
||||
public BundleFormat bundle_format {
|
||||
get;
|
||||
set;
|
||||
default = ESM;
|
||||
}
|
||||
|
||||
public TypeCheckMode type_check {
|
||||
get;
|
||||
set;
|
||||
default = FULL;
|
||||
}
|
||||
|
||||
public SourceMaps source_maps {
|
||||
get;
|
||||
set;
|
||||
@@ -471,6 +381,46 @@ namespace Frida {
|
||||
public sealed class WatchOptions : CompilerOptions {
|
||||
}
|
||||
|
||||
public enum OutputFormat {
|
||||
UNESCAPED,
|
||||
HEX_BYTES,
|
||||
C_STRING;
|
||||
|
||||
public static OutputFormat from_nick (string nick) throws Error {
|
||||
return Marshal.enum_from_nick<OutputFormat> (nick);
|
||||
}
|
||||
|
||||
public string to_nick () {
|
||||
return Marshal.enum_to_nick<OutputFormat> (this);
|
||||
}
|
||||
}
|
||||
|
||||
public enum BundleFormat {
|
||||
ESM,
|
||||
IIFE;
|
||||
|
||||
public static BundleFormat from_nick (string nick) throws Error {
|
||||
return Marshal.enum_from_nick<BundleFormat> (nick);
|
||||
}
|
||||
|
||||
public string to_nick () {
|
||||
return Marshal.enum_to_nick<BundleFormat> (this);
|
||||
}
|
||||
}
|
||||
|
||||
public enum TypeCheckMode {
|
||||
FULL,
|
||||
NONE;
|
||||
|
||||
public static TypeCheckMode from_nick (string nick) throws Error {
|
||||
return Marshal.enum_from_nick<TypeCheckMode> (nick);
|
||||
}
|
||||
|
||||
public string to_nick () {
|
||||
return Marshal.enum_to_nick<TypeCheckMode> (this);
|
||||
}
|
||||
}
|
||||
|
||||
public enum SourceMaps {
|
||||
INCLUDED,
|
||||
OMITTED;
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
INPUTS = [
|
||||
"agent-entrypoint.js",
|
||||
"agent-core.ts",
|
||||
"agent-warmup.js",
|
||||
"package.json",
|
||||
"package-lock.json",
|
||||
"tsconfig.json",
|
||||
"rollup.config.agent-core.ts",
|
||||
"rollup.config.typescript.ts",
|
||||
]
|
||||
|
||||
|
||||
def main(argv):
|
||||
output_dir, priv_dir, input_dir, npm, v8_mksnapshot = [Path(d).resolve() if d else None for d in argv[1:6]]
|
||||
host_os_family, host_arch, host_cpu_mode = argv[6:9]
|
||||
|
||||
try:
|
||||
generate_agent(output_dir, priv_dir, input_dir, npm, v8_mksnapshot, host_os_family, host_arch, host_cpu_mode)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(e, file=sys.stderr)
|
||||
print("Output:\n\t| " + "\n\t| ".join(e.output.strip().split("\n")), file=sys.stderr)
|
||||
sys.exit(2)
|
||||
except Exception as e:
|
||||
print(e, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def generate_agent(output_dir, priv_dir, input_dir, npm, v8_mksnapshot, host_os_family, host_arch, host_cpu_mode):
|
||||
entrypoint = input_dir / "agent-entrypoint.js"
|
||||
priv_dir.mkdir(exist_ok=True)
|
||||
for name in INPUTS:
|
||||
if name == "agent-entrypoint.js":
|
||||
continue
|
||||
shutil.copy(input_dir / name, priv_dir / name)
|
||||
|
||||
run_kwargs = {
|
||||
"cwd": priv_dir,
|
||||
"stdout": subprocess.PIPE,
|
||||
"stderr": subprocess.STDOUT,
|
||||
"encoding": "utf-8",
|
||||
"check": True,
|
||||
}
|
||||
|
||||
subprocess.run([npm, "install"], **run_kwargs)
|
||||
#subprocess.run([npm, "link", "/Users/oleavr/src/frida-compile"], **run_kwargs)
|
||||
|
||||
components = ["typescript", "agent-core"]
|
||||
for component in components:
|
||||
subprocess.run([
|
||||
npm, "run", "build:" + component,
|
||||
"--",
|
||||
"--environment", f"FRIDA_HOST_OS_FAMILY:{host_os_family},FRIDA_HOST_ARCH:{host_arch},FRIDA_HOST_CPU_MODE:{host_cpu_mode}",
|
||||
"--silent",
|
||||
],
|
||||
**run_kwargs)
|
||||
chunks = []
|
||||
for component in components:
|
||||
script = (priv_dir / f"{component}.js").read_text(encoding="utf-8")
|
||||
chunks.append(script)
|
||||
components_source = "\n".join(chunks)
|
||||
|
||||
agent = output_dir / "agent.js"
|
||||
snapshot = output_dir / "snapshot.bin"
|
||||
|
||||
if v8_mksnapshot is not None:
|
||||
shutil.copy(entrypoint, agent)
|
||||
(priv_dir / "embed.js").write_text(components_source, encoding="utf-8")
|
||||
subprocess.run([
|
||||
v8_mksnapshot,
|
||||
"--turbo-instruction-scheduling",
|
||||
f"--startup-blob={snapshot}",
|
||||
"embed.js",
|
||||
input_dir / "agent-warmup.js",
|
||||
],
|
||||
**run_kwargs)
|
||||
else:
|
||||
agent.write_text("\n".join([
|
||||
components_source,
|
||||
entrypoint.read_text(encoding="utf-8"),
|
||||
]), encoding="utf-8")
|
||||
snapshot.write_bytes(b"")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
@@ -0,0 +1,14 @@
|
||||
module github.com/frida/frida-core-compiler
|
||||
|
||||
go 1.24.3
|
||||
|
||||
require (
|
||||
github.com/evanw/esbuild v0.25.5
|
||||
github.com/frida/typescript-go v0.0.0-20250601212135-a4721f45f19c
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/go-json-experiment/json v0.0.0-20250517221953-25912455fbc8 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
|
||||
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/evanw/esbuild v0.25.5 h1:E+JpeY5S/1LFmnX1vtuZqUKT7qDVcfXdhzMhM3uIKFs=
|
||||
github.com/evanw/esbuild v0.25.5/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
|
||||
github.com/frida/typescript-go v0.0.0-20250601212135-a4721f45f19c h1:IWBI9AwSFLWymI4enx1jrAw0E4H2E+qHwsihv+AxSoE=
|
||||
github.com/frida/typescript-go v0.0.0-20250601212135-a4721f45f19c/go.mod h1:L4RMzD/pFBl7CqIf9XTrX41RO2xcok3OOVVcowv0H4k=
|
||||
github.com/go-json-experiment/json v0.0.0-20250517221953-25912455fbc8 h1:o8UqXPI6SVwQt04RGsqKp3qqmbOfTNMqDrWsc4O47kk=
|
||||
github.com/go-json-experiment/json v0.0.0-20250517221953-25912455fbc8/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/peter-evans/patience v0.3.0 h1:rX0JdJeepqdQl1Sk9c9uvorjYYzL2TfgLX1adqYm9cA=
|
||||
github.com/peter-evans/patience v0.3.0/go.mod h1:Kmxu5sY1NmBLFSStvXjX1wS9mIv7wMcP/ubucyMOAu0=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
+45
-41
@@ -1,61 +1,65 @@
|
||||
compiler_sources = files(
|
||||
'compiler.vala',
|
||||
'backend-glue.c',
|
||||
)
|
||||
|
||||
if have_compiler_backend
|
||||
compiler_mksnapshot = generate_compiler_snapshot ? v8_mksnapshot : ''
|
||||
|
||||
compiler_agent = custom_target('frida-compiler-agent',
|
||||
compiler_backend = custom_target('frida-compiler-backend',
|
||||
input: [
|
||||
'agent-entrypoint.js',
|
||||
'agent-core.ts',
|
||||
'agent-warmup.js',
|
||||
'go.mod',
|
||||
'go.sum',
|
||||
'backend.go',
|
||||
'backend.version',
|
||||
'tscompiler.go',
|
||||
'tsconfig.go',
|
||||
'shims.go',
|
||||
'package.json',
|
||||
'package-lock.json',
|
||||
'tsconfig.json',
|
||||
'rollup.config.agent-core.ts',
|
||||
'rollup.config.typescript.ts',
|
||||
],
|
||||
output: [
|
||||
'agent.js',
|
||||
'snapshot.bin',
|
||||
'frida-compiler-backend' + (compiler_backend_static_compilation ? '.a' : ('-raw' + shlib_suffix)),
|
||||
'frida-compiler-backend.h',
|
||||
],
|
||||
command: [
|
||||
python,
|
||||
files('generate-agent.py'),
|
||||
files('build-backend.py'),
|
||||
go_config,
|
||||
meson.current_build_dir(),
|
||||
'@PRIVATE_DIR@',
|
||||
meson.current_source_dir(),
|
||||
go,
|
||||
npm,
|
||||
compiler_mksnapshot,
|
||||
host_os_family,
|
||||
host_arch_gumjs,
|
||||
host_cpu_mode,
|
||||
'@INPUT@',
|
||||
],
|
||||
)
|
||||
compiler_data = custom_target('frida-data-compiler',
|
||||
input: [
|
||||
'compiler.resources.' + frida_profile,
|
||||
compiler_agent,
|
||||
],
|
||||
output: [
|
||||
'frida-data-compiler.vapi',
|
||||
'frida-data-compiler.h',
|
||||
'frida-data-compiler.c',
|
||||
'frida-data-compiler-blob' + resource_blob_extension,
|
||||
'frida-data-compiler-agent.js.br',
|
||||
'frida-data-compiler-snapshot.bin.br',
|
||||
],
|
||||
command: [
|
||||
resource_compiler_cmd_array,
|
||||
'-c', '@INPUT0@',
|
||||
'-o', meson.current_build_dir() / 'frida-data-compiler',
|
||||
'@INPUT1@',
|
||||
'@INPUT2@',
|
||||
],
|
||||
)
|
||||
compiler_sources += compiler_data
|
||||
if compiler_backend_static_compilation
|
||||
backend_libs += compiler_backend[0]
|
||||
else
|
||||
filename = 'frida-compiler-backend' + shlib_suffix
|
||||
identity = (host_os_family == 'darwin') ? 'FridaCompilerBackend' : filename
|
||||
compiler_backend_shlib = custom_target('frida-compiler-backend-shlib',
|
||||
input: compiler_backend[0],
|
||||
output: filename,
|
||||
command: post_process + ['shared-library', identity, 'elf-cleaner:off'],
|
||||
)
|
||||
|
||||
backend_vala_args_private += '--pkg=libbrotlidec'
|
||||
backend_deps_private += brotlidec_dep
|
||||
compiler_data = custom_target('frida-data-compiler',
|
||||
input: [
|
||||
'compiler.resources',
|
||||
compiler_backend_shlib,
|
||||
],
|
||||
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
|
||||
Generated
+105
-1746
File diff suppressed because it is too large.
Load diff
+31
-22
@@ -1,27 +1,36 @@
|
||||
{
|
||||
"name": "frida-compiler-agent",
|
||||
"name": "frida-compiler-shims",
|
||||
"version": "1.0.0",
|
||||
"description": "Agent used by Frida's compiler",
|
||||
"main": "agent.ts",
|
||||
"scripts": {
|
||||
"build:typescript": "rollup --config rollup.config.typescript.ts --configPlugin typescript",
|
||||
"build:agent-core": "rollup --config rollup.config.agent-core.ts --configPlugin typescript"
|
||||
},
|
||||
"description": "Shims used by Frida's compiler",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@frida/crosspath": "3.0.0",
|
||||
"frida-compile": "16.4.2",
|
||||
"frida-fs": "5.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@frida/rollup-plugin-node-polyfills": "^1.0.3",
|
||||
"@rollup/plugin-node-resolve": "^14.1.0",
|
||||
"@rollup/plugin-replace": "^4.0.0",
|
||||
"@rollup/plugin-typescript": "^8.3.4",
|
||||
"@types/frida-gum": "18.8.2",
|
||||
"@types/glob": "^8.0.0",
|
||||
"@types/node": "18.7.23",
|
||||
"glob": "^8.0.3",
|
||||
"rollup": "^2.79.1",
|
||||
"rollup-plugin-terser": "^7.0.2"
|
||||
"@frida/assert": "^3.0.2",
|
||||
"@frida/base64-js": "^2.0.3",
|
||||
"@frida/buffer": "^7.0.4",
|
||||
"@frida/crypto": "^1.0.1",
|
||||
"@frida/diagnostics_channel": "^1.0.0",
|
||||
"@frida/events": "^4.0.4",
|
||||
"@frida/http": "^4.0.2",
|
||||
"@frida/http-parser-js": "^1.0.0",
|
||||
"@frida/https": "^1.0.0",
|
||||
"@frida/ieee754": "^2.0.2",
|
||||
"@frida/net": "^5.0.0",
|
||||
"@frida/os": "^1.0.2",
|
||||
"@frida/path": "^2.0.3",
|
||||
"@frida/process": "^1.2.1",
|
||||
"@frida/punycode": "^3.0.0",
|
||||
"@frida/querystring": "^1.0.0",
|
||||
"@frida/readable-stream": "^4.1.3",
|
||||
"@frida/reserved-words": "^1.0.0",
|
||||
"@frida/stream": "^1.0.2",
|
||||
"@frida/string_decoder": "^2.0.0",
|
||||
"@frida/timers": "^3.0.0",
|
||||
"@frida/tty": "^1.0.0",
|
||||
"@frida/url": "^1.0.2",
|
||||
"@frida/util": "^1.0.3",
|
||||
"@frida/vm": "^2.0.0",
|
||||
"@types/frida-gum": "19.0.0",
|
||||
"@types/node": "18.19.103",
|
||||
"frida-fs": "7.0.0"
|
||||
}
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
import crosspath from "@frida/crosspath";
|
||||
import fs from "fs";
|
||||
import { sync as glob } from "glob";
|
||||
import replace from "@rollup/plugin-replace";
|
||||
import resolve from "@rollup/plugin-node-resolve";
|
||||
import typescript from "@rollup/plugin-typescript";
|
||||
import polyfills from "@frida/rollup-plugin-node-polyfills";
|
||||
import { terser } from "rollup-plugin-terser";
|
||||
import { defineConfig } from "rollup";
|
||||
import type rollup from "rollup";
|
||||
|
||||
export default defineConfig({
|
||||
input: "agent-core.ts",
|
||||
output: {
|
||||
file: "agent-core.js",
|
||||
format: "umd",
|
||||
name: "FridaCompilerAgentCore",
|
||||
generatedCode: {
|
||||
preset: "es2015",
|
||||
},
|
||||
strict: false,
|
||||
interop: "default",
|
||||
globals: {
|
||||
"typescript": "ts",
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
typescript(),
|
||||
replace({
|
||||
preventAssignment: true,
|
||||
values: computeSubstitutionValues(),
|
||||
}),
|
||||
stubTsImportPlugin(),
|
||||
polyfills(),
|
||||
resolve(),
|
||||
/*
|
||||
terser({
|
||||
ecma: 2020,
|
||||
compress: {
|
||||
module: true,
|
||||
global_defs: {
|
||||
"process.env.FRIDA_COMPILE": true
|
||||
},
|
||||
},
|
||||
mangle: {
|
||||
module: true,
|
||||
},
|
||||
}),
|
||||
*/
|
||||
],
|
||||
});
|
||||
|
||||
function computeSubstitutionValues() {
|
||||
const hostOsFamily = process.env.FRIDA_HOST_OS_FAMILY;
|
||||
if (hostOsFamily === undefined) {
|
||||
throw new Error("missing FRIDA_HOST_OS_FAMILY");
|
||||
}
|
||||
|
||||
const hostArch = process.env.FRIDA_HOST_ARCH;
|
||||
if (hostArch === undefined) {
|
||||
throw new Error("missing FRIDA_HOST_ARCH");
|
||||
}
|
||||
|
||||
const hostCpuMode = process.env.FRIDA_HOST_CPU_MODE;
|
||||
if (hostCpuMode === undefined) {
|
||||
throw new Error("missing FRIDA_HOST_CPU_MODE");
|
||||
}
|
||||
|
||||
const outputDir = __dirname;
|
||||
|
||||
const compilerDir = crosspath.join(__dirname, "node_modules", "frida-compile");
|
||||
let usingLinkedCompiler = false;
|
||||
try {
|
||||
usingLinkedCompiler = fs.statSync(crosspath.join(compilerDir, "node_modules")).isDirectory();
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
const assetParentDir = usingLinkedCompiler ? compilerDir : outputDir;
|
||||
const assetModulesDir = crosspath.join(assetParentDir, "node_modules");
|
||||
|
||||
const shimDirs = [
|
||||
["@frida", "assert"],
|
||||
["@frida", "base64-js"],
|
||||
["@frida", "buffer"],
|
||||
["@frida", "crypto"],
|
||||
["@frida", "diagnostics_channel"],
|
||||
["@frida", "events"],
|
||||
["@frida", "http"],
|
||||
["@frida", "http-parser-js"],
|
||||
["@frida", "https"],
|
||||
["@frida", "ieee754"],
|
||||
["@frida", "net"],
|
||||
["@frida", "os"],
|
||||
["@frida", "path"],
|
||||
["@frida", "process"],
|
||||
["@frida", "punycode"],
|
||||
["@frida", "querystring"],
|
||||
["@frida", "readable-stream"],
|
||||
["@frida", "stream"],
|
||||
["@frida", "string_decoder"],
|
||||
["@frida", "timers"],
|
||||
["@frida", "tty"],
|
||||
["@frida", "url"],
|
||||
["@frida", "util"],
|
||||
["@frida", "vm"],
|
||||
["frida-fs"],
|
||||
];
|
||||
const typeDirs = [
|
||||
["@types", "node"],
|
||||
["@types", "frida-gum"],
|
||||
];
|
||||
|
||||
const assets: string[] = [];
|
||||
for (const shimDir of shimDirs) {
|
||||
assets.push(...glob(crosspath.join(assetModulesDir, ...shimDir, "package.json")));
|
||||
assets.push(...glob(crosspath.join(assetModulesDir, ...shimDir, "**", "*.js")));
|
||||
}
|
||||
for (const typeDir of typeDirs) {
|
||||
assets.push(...glob(crosspath.join(assetModulesDir, ...typeDir, "package.json")));
|
||||
assets.push(...glob(crosspath.join(assetModulesDir, ...typeDir, "**", "*.d.ts")));
|
||||
}
|
||||
assets.push(...glob(crosspath.join(compilerDir, "ext", "lib.es*.d.ts")));
|
||||
assets.push(...glob(crosspath.join(compilerDir, "ext", "lib.decorators*.d.ts")));
|
||||
|
||||
const ignoredAssetFiles = new Set([
|
||||
"@frida/process/browser.js",
|
||||
]);
|
||||
|
||||
const agentDirectories = new Set<string>();
|
||||
const agentFiles = new Map<string, string>();
|
||||
for (const assetPath of assets) {
|
||||
let assetRelpath = assetPath.substring(assetParentDir.length);
|
||||
if (usingLinkedCompiler && assetRelpath.startsWith("/ext/")) {
|
||||
assetRelpath = crosspath.join("/", "node_modules", "frida-compile", assetRelpath.substring(1));
|
||||
}
|
||||
|
||||
const identifier = assetRelpath.split("/").slice(2).join("/");
|
||||
if (ignoredAssetFiles.has(identifier)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let currentDir = crosspath.dirname(assetRelpath);
|
||||
do {
|
||||
agentDirectories.add(currentDir);
|
||||
currentDir = crosspath.dirname(currentDir);
|
||||
} while (currentDir !== "/");
|
||||
agentFiles.set(assetRelpath, fs.readFileSync(assetPath, "utf-8"));
|
||||
}
|
||||
|
||||
const orderedAgentDirectories = Array.from(agentDirectories);
|
||||
orderedAgentDirectories.sort();
|
||||
|
||||
const orderedAgentFiles = Array.from(agentFiles);
|
||||
orderedAgentFiles.sort((a, b) => a[0].localeCompare(b[0]));
|
||||
|
||||
return {
|
||||
"Frida.version": "'0.0.0'",
|
||||
"Process.id": "1",
|
||||
"Process.platform": `'${hostOsFamily}'`,
|
||||
"Process.arch": `'${hostArch}'`,
|
||||
"Process.pointerSize": (hostCpuMode === "64") ? "8" : "4",
|
||||
"__agentDirectories__": JSON.stringify(orderedAgentDirectories, null, 4),
|
||||
"__agentFiles__": JSON.stringify(orderedAgentFiles, null, 4),
|
||||
};
|
||||
}
|
||||
|
||||
function stubTsImportPlugin(): rollup.Plugin {
|
||||
return {
|
||||
name: "stub-ts-import",
|
||||
async resolveId(source, importer, options) {
|
||||
if (crosspath.basename(source) === "typescript.js") {
|
||||
return {
|
||||
id: "typescript",
|
||||
external: true,
|
||||
meta: {},
|
||||
moduleSideEffects: false,
|
||||
syntheticNamedExports: false
|
||||
};
|
||||
}
|
||||
|
||||
return this.resolve(source, importer, { skipSelf: true, ...options });
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import path from "path";
|
||||
import { defineConfig } from "rollup";
|
||||
|
||||
export default defineConfig({
|
||||
input: path.join("node_modules", "frida-compile", "ext", "typescript.js"),
|
||||
output: {
|
||||
file: "typescript.js",
|
||||
format: "umd",
|
||||
name: "ts",
|
||||
generatedCode: {
|
||||
preset: "es2015",
|
||||
},
|
||||
strict: false,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,196 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
esbuild "github.com/evanw/esbuild/pkg/api"
|
||||
)
|
||||
|
||||
var nodeGlobals = `
|
||||
export { Buffer } from 'node:buffer';
|
||||
export { default as process } from 'node:process';
|
||||
`
|
||||
|
||||
//go:embed node_modules/@frida/*/package.json
|
||||
//go:embed node_modules/@frida/*/*.js
|
||||
//go:embed node_modules/@frida/*/*/*.js
|
||||
//go:embed node_modules/@frida/*/*/*/*.js
|
||||
//go:embed node_modules/frida-fs/package.json
|
||||
//go:embed node_modules/frida-fs/*/*.js
|
||||
var embeddedShims embed.FS
|
||||
|
||||
func makeFridaShimsPlugin() esbuild.Plugin {
|
||||
const (
|
||||
nsBuiltins = "frida-builtins"
|
||||
nsShim = "frida-shim"
|
||||
)
|
||||
|
||||
const (
|
||||
builtinFilter = `^frida-builtins://(.+)$`
|
||||
shimFilter = `^(assert|base64-js|buffer|crypto|diagnostics_channel|events|fs|http|https|http-parser-js|ieee754|net|os|path|process|punycode|querystring|readable-stream|stream|string_decoder|timers|tty|url|util|vm)$`
|
||||
shimNodePrefixFilter = `^node:(assert|buffer|crypto|diagnostics_channel|events|fs|http|https|net|os|path|process|punycode|querystring|stream|string_decoder|timers|tty|url|util|vm)$`
|
||||
)
|
||||
|
||||
return esbuild.Plugin{
|
||||
Name: "frida-custom-shims",
|
||||
Setup: func(build esbuild.PluginBuild) {
|
||||
build.OnResolve(esbuild.OnResolveOptions{Filter: builtinFilter},
|
||||
func(args esbuild.OnResolveArgs) (esbuild.OnResolveResult, error) {
|
||||
return esbuild.OnResolveResult{
|
||||
Path: strings.TrimPrefix(args.Path, "frida-builtins://"),
|
||||
Namespace: nsBuiltins,
|
||||
SideEffects: esbuild.SideEffectsFalse,
|
||||
}, nil
|
||||
})
|
||||
|
||||
build.OnLoad(esbuild.OnLoadOptions{Filter: ".*", Namespace: nsBuiltins},
|
||||
func(args esbuild.OnLoadArgs) (esbuild.OnLoadResult, error) {
|
||||
if args.Path != "/node-globals.js" {
|
||||
return esbuild.OnLoadResult{
|
||||
Errors: []esbuild.Message{{Text: "Unexpected path: " + args.Path}},
|
||||
}, nil
|
||||
}
|
||||
return esbuild.OnLoadResult{
|
||||
Contents: &nodeGlobals,
|
||||
Loader: esbuild.LoaderJS,
|
||||
}, nil
|
||||
})
|
||||
|
||||
registerShimResolve := func(pattern string) {
|
||||
build.OnResolve(esbuild.OnResolveOptions{Filter: pattern},
|
||||
func(args esbuild.OnResolveArgs) (esbuild.OnResolveResult, error) {
|
||||
return makeResolveResult(nsShim, args.Path)
|
||||
})
|
||||
}
|
||||
registerShimResolve(shimFilter)
|
||||
registerShimResolve(shimNodePrefixFilter)
|
||||
|
||||
build.OnResolve(esbuild.OnResolveOptions{Filter: ".*", Namespace: nsShim},
|
||||
func(args esbuild.OnResolveArgs) (esbuild.OnResolveResult, error) {
|
||||
if strings.HasPrefix(args.Path, ".") {
|
||||
abs := filepath.ToSlash(filepath.Join(filepath.Dir(args.Importer), args.Path))
|
||||
return esbuild.OnResolveResult{
|
||||
Path: abs,
|
||||
Namespace: nsShim,
|
||||
SideEffects: esbuild.SideEffectsFalse,
|
||||
}, nil
|
||||
}
|
||||
return makeResolveResult(nsShim, args.Path)
|
||||
})
|
||||
|
||||
build.OnLoad(esbuild.OnLoadOptions{Filter: ".*", Namespace: nsShim},
|
||||
func(args esbuild.OnLoadArgs) (esbuild.OnLoadResult, error) {
|
||||
data, err := embeddedShims.ReadFile(filepath.ToSlash(args.Path))
|
||||
if err != nil {
|
||||
return esbuild.OnLoadResult{
|
||||
Errors: []esbuild.Message{{
|
||||
Text: fmt.Sprintf("Error reading shim file %q: %v", args.Path, err),
|
||||
}},
|
||||
}, nil
|
||||
}
|
||||
|
||||
content := string(data)
|
||||
return esbuild.OnLoadResult{
|
||||
Contents: &content,
|
||||
Loader: loaderFor(args.Path),
|
||||
}, nil
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func makeResolveResult(ns string, module string) (esbuild.OnResolveResult, error) {
|
||||
path, errs := resolveShim(module)
|
||||
if len(errs) > 0 {
|
||||
return esbuild.OnResolveResult{Errors: errs}, nil
|
||||
}
|
||||
return esbuild.OnResolveResult{
|
||||
Path: path,
|
||||
Namespace: ns,
|
||||
SideEffects: esbuild.SideEffectsFalse,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func loaderFor(path string) esbuild.Loader {
|
||||
switch ext := filepath.Ext(path); ext {
|
||||
case ".js", ".mjs", ".cjs":
|
||||
return esbuild.LoaderJS
|
||||
case ".json":
|
||||
return esbuild.LoaderJSON
|
||||
default:
|
||||
panic(fmt.Sprintf("frida-shims: unsupported file type %q", ext))
|
||||
}
|
||||
}
|
||||
|
||||
var shimMap = map[string]string{
|
||||
"assert": "@frida/assert",
|
||||
"base64-js": "@frida/base64-js",
|
||||
"buffer": "@frida/buffer",
|
||||
"crypto": "@frida/crypto",
|
||||
"diagnostics_channel": "@frida/diagnostics_channel",
|
||||
"events": "@frida/events",
|
||||
"fs": "frida-fs",
|
||||
"http": "@frida/http",
|
||||
"https": "@frida/https",
|
||||
"http-parser-js": "@frida/http-parser-js",
|
||||
"ieee754": "@frida/ieee754",
|
||||
"net": "@frida/net",
|
||||
"os": "@frida/os",
|
||||
"path": "@frida/path",
|
||||
"process": "@frida/process",
|
||||
"punycode": "@frida/punycode",
|
||||
"querystring": "@frida/querystring",
|
||||
"readable-stream": "@frida/readable-stream",
|
||||
"stream": "@frida/stream",
|
||||
"string_decoder": "@frida/string_decoder",
|
||||
"timers": "@frida/timers",
|
||||
"tty": "@frida/tty",
|
||||
"url": "@frida/url",
|
||||
"util": "@frida/util",
|
||||
"vm": "@frida/vm",
|
||||
}
|
||||
|
||||
type PackageJSON struct {
|
||||
Main string `json:"main"`
|
||||
Module string `json:"module"`
|
||||
}
|
||||
|
||||
func resolveShim(shimName string) (string, []esbuild.Message) {
|
||||
var subDir string
|
||||
if strings.HasPrefix(shimName, "@frida/") {
|
||||
subDir = shimName
|
||||
} else {
|
||||
actualShimName := strings.TrimPrefix(shimName, "node:")
|
||||
|
||||
var ok bool
|
||||
subDir, ok = shimMap[actualShimName]
|
||||
if !ok {
|
||||
return "", []esbuild.Message{{Text: "Unknown shim: " + actualShimName}}
|
||||
}
|
||||
}
|
||||
|
||||
shimPackageDir := filepath.Join("node_modules", subDir)
|
||||
packageJSONPath := filepath.Join(shimPackageDir, "package.json")
|
||||
|
||||
pkgJSONBytes, _ := embeddedShims.ReadFile(filepath.ToSlash(packageJSONPath))
|
||||
|
||||
var pkg PackageJSON
|
||||
json.Unmarshal(pkgJSONBytes, &pkg)
|
||||
|
||||
entryFile := ""
|
||||
if pkg.Module != "" {
|
||||
entryFile = pkg.Module
|
||||
} else if pkg.Main != "" {
|
||||
entryFile = pkg.Main
|
||||
} else {
|
||||
entryFile = "index.js"
|
||||
}
|
||||
|
||||
resolvedEntryPath := filepath.Join(shimPackageDir, entryFile)
|
||||
|
||||
return filepath.ToSlash(resolvedEntryPath), nil
|
||||
}
|
||||
@@ -0,0 +1,448 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
"maps"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/frida/typescript-go/pkg/ast"
|
||||
"github.com/frida/typescript-go/pkg/bundled"
|
||||
"github.com/frida/typescript-go/pkg/compiler"
|
||||
"github.com/frida/typescript-go/pkg/core"
|
||||
"github.com/frida/typescript-go/pkg/tsoptions"
|
||||
"github.com/frida/typescript-go/pkg/tspath"
|
||||
"github.com/frida/typescript-go/pkg/vfs"
|
||||
"github.com/frida/typescript-go/pkg/vfs/iovfs"
|
||||
"github.com/frida/typescript-go/pkg/vfs/osvfs"
|
||||
)
|
||||
|
||||
//go:embed node_modules/@types/*/package.json
|
||||
//go:embed node_modules/@types/*/*.d.ts
|
||||
//go:embed node_modules/@types/*/*/*.d.ts
|
||||
var embeddedTypes embed.FS
|
||||
|
||||
type TSCompiler struct {
|
||||
projectRoot string
|
||||
entrypoint string
|
||||
loadCompilerOptions LoadCompilerOptionsHandler
|
||||
fs vfs.FS
|
||||
captureFs *captureFS
|
||||
|
||||
mu sync.Mutex
|
||||
options *core.CompilerOptions
|
||||
program *compiler.Program
|
||||
programErr error
|
||||
forceFreshProgram bool
|
||||
mtimes map[tspath.Path]time.Time
|
||||
inputDirs, inputFiles []string
|
||||
pendingDirs, pendingFiles []string
|
||||
}
|
||||
|
||||
type LoadCompilerOptionsHandler func(host tsoptions.ParseConfigHost) (*core.CompilerOptions, string, error)
|
||||
|
||||
func NewTSCompiler(projectRoot, entrypoint string, loadCompilerOptions LoadCompilerOptionsHandler) *TSCompiler {
|
||||
captureFs := newCaptureFS(osvfs.FS())
|
||||
fs := newTypesFS(bundled.WrapFS(captureFs), projectRoot)
|
||||
|
||||
return &TSCompiler{
|
||||
projectRoot: projectRoot,
|
||||
entrypoint: tspath.NormalizePath(entrypoint),
|
||||
loadCompilerOptions: loadCompilerOptions,
|
||||
fs: fs,
|
||||
captureFs: captureFs,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *TSCompiler) resetProgramState() {
|
||||
c.options = nil
|
||||
c.program = nil
|
||||
c.programErr = nil
|
||||
c.forceFreshProgram = false
|
||||
c.mtimes = nil
|
||||
}
|
||||
|
||||
func (c *TSCompiler) EnsureProgramUpToDate() error {
|
||||
opts, _, err := c.loadCompilerOptions(c)
|
||||
if err != nil {
|
||||
c.resetProgramState()
|
||||
c.recomputeInputs()
|
||||
return err
|
||||
}
|
||||
|
||||
var prog *compiler.Program
|
||||
var progErr error
|
||||
|
||||
if c.forceFreshProgram || c.program == nil || opts != c.options {
|
||||
c.forceFreshProgram = false
|
||||
prog, progErr = c.createProgram(opts)
|
||||
} else {
|
||||
prog, progErr = c.updateProgram(c.program, opts)
|
||||
}
|
||||
|
||||
c.program = prog
|
||||
c.programErr = progErr
|
||||
c.options = opts
|
||||
|
||||
c.recomputeInputs()
|
||||
|
||||
return progErr
|
||||
}
|
||||
|
||||
func (c *TSCompiler) createProgram(options *core.CompilerOptions) (*compiler.Program, error) {
|
||||
host := compiler.NewCompilerHost(options, c.projectRoot, c.fs, bundled.LibPath())
|
||||
|
||||
program := compiler.NewProgram(compiler.ProgramOptions{
|
||||
RootFiles: []string{c.entrypoint},
|
||||
Host: host,
|
||||
Options: options,
|
||||
})
|
||||
|
||||
c.updateMtimes(program)
|
||||
|
||||
return program, nil
|
||||
}
|
||||
|
||||
func (c *TSCompiler) updateProgram(old *compiler.Program, options *core.CompilerOptions) (*compiler.Program, error) {
|
||||
newMtimes, err := c.collectMtimes(old)
|
||||
if err != nil {
|
||||
return c.createProgram(options)
|
||||
}
|
||||
|
||||
updated := []tspath.Path{}
|
||||
newProg := old
|
||||
for path, mtime := range newMtimes {
|
||||
if !mtime.Equal(c.mtimes[path]) {
|
||||
var reused bool
|
||||
newProg, reused = newProg.UpdateProgram(path)
|
||||
updated = append(updated, path)
|
||||
if !reused {
|
||||
c.updateMtimes(newProg)
|
||||
return newProg, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
c.mtimes = newMtimes
|
||||
return newProg, nil
|
||||
}
|
||||
|
||||
func (c *TSCompiler) updateMtimes(p *compiler.Program) {
|
||||
mt, _ := c.collectMtimes(p)
|
||||
c.mtimes = mt
|
||||
}
|
||||
|
||||
func (c *TSCompiler) collectMtimes(p *compiler.Program) (map[tspath.Path]time.Time, error) {
|
||||
mt := make(map[tspath.Path]time.Time, len(p.SourceFiles()))
|
||||
|
||||
for _, sf := range p.SourceFiles() {
|
||||
name := sf.FileName()
|
||||
if isBundled(name) {
|
||||
continue
|
||||
}
|
||||
info := c.fs.Stat(name)
|
||||
if info == nil {
|
||||
return nil, fmt.Errorf("File %q disappeared", name)
|
||||
}
|
||||
mt[sf.Path()] = info.ModTime()
|
||||
}
|
||||
|
||||
return mt, nil
|
||||
}
|
||||
|
||||
func isBundled(name string) bool {
|
||||
return strings.HasPrefix(name, "bundled://")
|
||||
}
|
||||
|
||||
func (c *TSCompiler) WatchDirs() []string {
|
||||
if c.inputDirs != nil {
|
||||
return c.inputDirs
|
||||
}
|
||||
return c.pendingDirs
|
||||
}
|
||||
|
||||
func (c *TSCompiler) WatchFiles() []string {
|
||||
if c.inputFiles != nil {
|
||||
return c.inputFiles
|
||||
}
|
||||
return c.pendingFiles
|
||||
}
|
||||
|
||||
func (c *TSCompiler) CommitPendingInputs() {
|
||||
c.inputDirs, c.pendingDirs = c.pendingDirs, nil
|
||||
c.inputFiles, c.pendingFiles = c.pendingFiles, nil
|
||||
}
|
||||
|
||||
func (c *TSCompiler) DropPendingInputs() {
|
||||
c.pendingDirs, c.pendingFiles = nil, nil
|
||||
}
|
||||
|
||||
func (c *TSCompiler) recomputeInputs() {
|
||||
var capHint int
|
||||
if c.program != nil {
|
||||
capHint = 1 + len(c.program.GetSourceFiles())
|
||||
} else {
|
||||
capHint = 1
|
||||
}
|
||||
|
||||
files := make([]string, 0, capHint)
|
||||
files = append(files, filepath.Join(c.projectRoot, "tsconfig.json"))
|
||||
if c.program != nil {
|
||||
for _, sf := range c.program.GetSourceFiles() {
|
||||
name := sf.FileName()
|
||||
if !isBundled(name) {
|
||||
files = append(files, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(files)
|
||||
|
||||
c.pendingDirs = uniqueDirs(files)
|
||||
c.pendingFiles = files
|
||||
}
|
||||
|
||||
func (c *TSCompiler) Compile(filePathToCompile string) (string, []*ast.Diagnostic, error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.programErr != nil {
|
||||
return "", nil, c.programErr
|
||||
}
|
||||
program := c.program
|
||||
|
||||
var targetSourceFile *ast.SourceFile
|
||||
normalizedFilePathToCompile := tspath.NormalizePath(filePathToCompile)
|
||||
for _, sf := range program.GetSourceFiles() {
|
||||
if sf.FileName() == normalizedFilePathToCompile {
|
||||
targetSourceFile = sf
|
||||
break
|
||||
}
|
||||
}
|
||||
if targetSourceFile == nil {
|
||||
return "", nil, fmt.Errorf("TypeScript source file not found in program: %s", filePathToCompile)
|
||||
}
|
||||
|
||||
c.captureFs.ClearOutputs()
|
||||
|
||||
res := program.Emit(compiler.EmitOptions{
|
||||
TargetSourceFile: targetSourceFile,
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
diagnostics := program.GetSyntacticDiagnostics(ctx, targetSourceFile)
|
||||
if len(diagnostics) == 0 {
|
||||
diagnostics = append(diagnostics, program.GetBindDiagnostics(ctx, targetSourceFile)...)
|
||||
}
|
||||
if len(diagnostics) == 0 {
|
||||
diagnostics = append(diagnostics, program.GetOptionsDiagnostics(ctx)...)
|
||||
}
|
||||
if len(diagnostics) == 0 {
|
||||
diagnostics = append(diagnostics, program.GetGlobalDiagnostics(ctx)...)
|
||||
}
|
||||
if len(diagnostics) == 0 {
|
||||
semanticDiags := program.GetSemanticDiagnostics(ctx, targetSourceFile)
|
||||
for _, d := range semanticDiags {
|
||||
switch d.Code() {
|
||||
case
|
||||
2305, // Module '{0}' has no exported member '{1}'.
|
||||
2306, // File '{0}' is not a module.
|
||||
2307: // Cannot find module '{0}' or its corresponding type declarations.
|
||||
c.forceFreshProgram = true
|
||||
}
|
||||
}
|
||||
diagnostics = append(diagnostics, semanticDiags...)
|
||||
}
|
||||
|
||||
if res.EmitSkipped {
|
||||
errMsg := "TypeScript compilation failed and was skipped"
|
||||
if len(diagnostics) == 0 {
|
||||
errMsg = fmt.Sprintf("TypeScript compilation failed and was skipped for %s, but no diagnostics reported", filePathToCompile)
|
||||
}
|
||||
return "", diagnostics, fmt.Errorf(errMsg)
|
||||
}
|
||||
|
||||
var compiledJS string
|
||||
var foundJSOutput bool
|
||||
for path, content := range c.captureFs.GetOutputs() {
|
||||
ext := filepath.Ext(path)
|
||||
if ext == ".js" {
|
||||
compiledJS = content
|
||||
foundJSOutput = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundJSOutput {
|
||||
if len(diagnostics) == 0 {
|
||||
return "", nil, fmt.Errorf("TypeScript compilation for %s seemed to succeed (emit not skipped, no diagnostics) but no .js output file was captured. Captured outputs: %v", filePathToCompile, c.captureFs.GetOutputs())
|
||||
}
|
||||
return "", diagnostics, fmt.Errorf("No .js output file was captured for %s. Captured outputs: %v", filePathToCompile, c.captureFs.GetOutputs())
|
||||
}
|
||||
|
||||
return compiledJS, diagnostics, nil
|
||||
}
|
||||
|
||||
func uniqueDirs(files []string) []string {
|
||||
m := make(map[string]struct{}, len(files))
|
||||
for _, f := range files {
|
||||
m[filepath.Dir(f)] = struct{}{}
|
||||
}
|
||||
|
||||
out := make([]string, 0, len(m))
|
||||
for d := range m {
|
||||
out = append(out, d)
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// FS implements ParseConfigHost.
|
||||
func (c *TSCompiler) FS() vfs.FS {
|
||||
return c.fs
|
||||
}
|
||||
|
||||
// GetCurrentDirectory implements ParseConfigHost.
|
||||
func (c *TSCompiler) GetCurrentDirectory() string {
|
||||
return c.projectRoot
|
||||
}
|
||||
|
||||
type captureFS struct {
|
||||
vfs.FS
|
||||
outputs map[string]string
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
var _ vfs.FS = (*captureFS)(nil)
|
||||
|
||||
func newCaptureFS(inner vfs.FS) *captureFS {
|
||||
return &captureFS{
|
||||
FS: inner,
|
||||
outputs: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *captureFS) ClearOutputs() {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
c.outputs = make(map[string]string)
|
||||
}
|
||||
|
||||
func (c *captureFS) GetOutputs() map[string]string {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
snap := make(map[string]string, len(c.outputs))
|
||||
for k, v := range c.outputs {
|
||||
snap[k] = v
|
||||
}
|
||||
return snap
|
||||
}
|
||||
|
||||
func (c *captureFS) WriteFile(path string, data string, writeByteOrderMark bool) error {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
c.outputs[path] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *captureFS) Remove(path string) error {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
delete(c.outputs, path)
|
||||
return nil
|
||||
}
|
||||
|
||||
type typesFS struct {
|
||||
vfs.FS
|
||||
projectRoot string
|
||||
types vfs.FS
|
||||
}
|
||||
|
||||
var _ vfs.FS = (*typesFS)(nil)
|
||||
|
||||
func newTypesFS(inner vfs.FS, projectRoot string) *typesFS {
|
||||
return &typesFS{
|
||||
FS: inner,
|
||||
projectRoot: projectRoot,
|
||||
types: iovfs.From(embeddedTypes, true),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *typesFS) DirectoryExists(path string) bool {
|
||||
embeddedPath := t.resolveEmbeddedPath(path)
|
||||
if embeddedPath != "" {
|
||||
if t.types.DirectoryExists(embeddedPath) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return t.FS.DirectoryExists(path)
|
||||
}
|
||||
|
||||
func (t *typesFS) GetAccessibleEntries(path string) vfs.Entries {
|
||||
embeddedPath := t.resolveEmbeddedPath(path)
|
||||
|
||||
var embeddedEntries vfs.Entries
|
||||
if embeddedPath != "" {
|
||||
embeddedEntries = t.types.GetAccessibleEntries(embeddedPath)
|
||||
}
|
||||
|
||||
otherEntries := t.FS.GetAccessibleEntries(path)
|
||||
|
||||
return vfs.Entries{
|
||||
Files: mergeAndSort(embeddedEntries.Files, otherEntries.Files),
|
||||
Directories: mergeAndSort(embeddedEntries.Directories, otherEntries.Directories),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *typesFS) FileExists(path string) bool {
|
||||
embeddedPath := t.resolveEmbeddedPath(path)
|
||||
if embeddedPath != "" {
|
||||
if t.types.FileExists(embeddedPath) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return t.FS.FileExists(path)
|
||||
}
|
||||
|
||||
func (t *typesFS) ReadFile(path string) (contents string, ok bool) {
|
||||
contents, ok = t.FS.ReadFile(path)
|
||||
if ok {
|
||||
return contents, ok
|
||||
}
|
||||
|
||||
embeddedPath := t.resolveEmbeddedPath(path)
|
||||
if embeddedPath != "" {
|
||||
contents, ok = t.types.ReadFile(embeddedPath)
|
||||
}
|
||||
|
||||
return contents, ok
|
||||
}
|
||||
|
||||
func (c *typesFS) resolveEmbeddedPath(path string) string {
|
||||
rel, err := filepath.Rel(c.projectRoot, path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return "/" + filepath.ToSlash(rel)
|
||||
}
|
||||
|
||||
func mergeAndSort(a, b []string) []string {
|
||||
m := make(map[string]struct{}, len(a)+len(b))
|
||||
for _, v := range append(a, b...) {
|
||||
m[v] = struct{}{}
|
||||
}
|
||||
return slices.Sorted(maps.Keys(m))
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
tscore "github.com/frida/typescript-go/pkg/core"
|
||||
"github.com/frida/typescript-go/pkg/tsoptions"
|
||||
"github.com/frida/typescript-go/pkg/tspath"
|
||||
)
|
||||
|
||||
type TSConfigCache struct {
|
||||
projectRoot string
|
||||
sourceMap bool
|
||||
tsconfigPath string
|
||||
onChange ConfigChangeCallback
|
||||
|
||||
mu sync.Mutex
|
||||
lastModTime time.Time
|
||||
cachedOpts *tscore.CompilerOptions
|
||||
lastText string
|
||||
}
|
||||
|
||||
type ConfigChangeCallback func()
|
||||
|
||||
func NewTSConfigCache(
|
||||
projectRoot string,
|
||||
sourceMap bool,
|
||||
callback ConfigChangeCallback,
|
||||
) *TSConfigCache {
|
||||
return &TSConfigCache{
|
||||
projectRoot: projectRoot,
|
||||
sourceMap: sourceMap,
|
||||
tsconfigPath: filepath.Join(projectRoot, "tsconfig.json"),
|
||||
lastModTime: time.Time{},
|
||||
cachedOpts: nil,
|
||||
lastText: "",
|
||||
onChange: callback,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *TSConfigCache) GetCompilerOptions(
|
||||
host tsoptions.ParseConfigHost,
|
||||
) (*tscore.CompilerOptions, string, error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
fs := host.FS()
|
||||
|
||||
info := fs.Stat(c.tsconfigPath)
|
||||
var currentModTime time.Time
|
||||
if info != nil {
|
||||
currentModTime = info.ModTime()
|
||||
} else {
|
||||
currentModTime = time.Time{}
|
||||
}
|
||||
|
||||
if c.cachedOpts != nil && currentModTime.Equal(c.lastModTime) {
|
||||
return c.cachedOpts, c.lastText, nil
|
||||
}
|
||||
|
||||
var tsconfigText string
|
||||
if info != nil {
|
||||
data, ok := fs.ReadFile(c.tsconfigPath)
|
||||
if !ok {
|
||||
return nil, "", fmt.Errorf("Unable to read %q", c.tsconfigPath)
|
||||
}
|
||||
tsconfigText = string(data)
|
||||
} else {
|
||||
tsconfigText = "{}"
|
||||
}
|
||||
|
||||
parsedSource := tsoptions.NewTsconfigSourceFileFromFilePath(
|
||||
tspath.NormalizePath(c.tsconfigPath),
|
||||
tspath.ToPath(c.tsconfigPath, "", fs.UseCaseSensitiveFileNames()),
|
||||
tsconfigText,
|
||||
)
|
||||
parsedCommandLine := tsoptions.ParseJsonSourceFileConfigFileContent(
|
||||
parsedSource,
|
||||
host,
|
||||
c.projectRoot,
|
||||
nil,
|
||||
c.tsconfigPath,
|
||||
nil, nil, nil,
|
||||
)
|
||||
|
||||
if len(parsedCommandLine.Errors) > 0 {
|
||||
var msgs []string
|
||||
for _, diag := range parsedCommandLine.Errors {
|
||||
msgs = append(msgs, diag.Message())
|
||||
}
|
||||
return nil, "", fmt.Errorf(
|
||||
"Failed to parse %s: %s",
|
||||
c.tsconfigPath,
|
||||
strings.Join(msgs, "; "),
|
||||
)
|
||||
}
|
||||
|
||||
newOpts := parsedCommandLine.CompilerOptions()
|
||||
newOpts.Module = tscore.ModuleKindNode16
|
||||
newOpts.ModuleResolution = tscore.ModuleResolutionKindNode16
|
||||
newOpts.NoEmit = tscore.TSFalse
|
||||
|
||||
sourceMapOptVal := boolToTristate(c.sourceMap)
|
||||
newOpts.SourceMap = sourceMapOptVal
|
||||
newOpts.InlineSourceMap = sourceMapOptVal
|
||||
|
||||
if newOpts.Target == tscore.ScriptTargetNone {
|
||||
newOpts.Target = tscore.ScriptTargetES2022
|
||||
}
|
||||
if newOpts.Lib == nil {
|
||||
newOpts.Lib = []string{"lib.es2022.d.ts"}
|
||||
}
|
||||
if newOpts.SkipLibCheck == tscore.TSUnknown {
|
||||
newOpts.SkipLibCheck = tscore.TSTrue
|
||||
}
|
||||
if newOpts.Strict == tscore.TSUnknown {
|
||||
newOpts.Strict = tscore.TSTrue
|
||||
}
|
||||
|
||||
tsconfigData, _ := json.Marshal(parsedCommandLine.ParsedConfig)
|
||||
tsconfigText = string(tsconfigData)
|
||||
|
||||
hadPreviousOpts := c.cachedOpts != nil
|
||||
optsChanged := true
|
||||
if c.cachedOpts != nil && reflect.DeepEqual(newOpts, c.cachedOpts) {
|
||||
optsChanged = false
|
||||
}
|
||||
|
||||
if optsChanged {
|
||||
c.cachedOpts = newOpts
|
||||
c.lastText = tsconfigText
|
||||
}
|
||||
c.lastModTime = currentModTime
|
||||
|
||||
c.mu.Unlock()
|
||||
defer c.mu.Lock()
|
||||
|
||||
if hadPreviousOpts && optsChanged {
|
||||
if c.onChange != nil {
|
||||
c.onChange()
|
||||
}
|
||||
}
|
||||
|
||||
return newOpts, tsconfigText, nil
|
||||
}
|
||||
|
||||
func boolToTristate(val bool) tscore.Tristate {
|
||||
if val {
|
||||
return tscore.TSTrue
|
||||
}
|
||||
return tscore.TSFalse
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2020",
|
||||
"lib": ["es2020"],
|
||||
"module": "ES2020",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ const substratePidsPending = new Map();
|
||||
rpc.exports = {
|
||||
dispose() {
|
||||
if (suspendedPids.size > 0) {
|
||||
const kill = new NativeFunction(Module.getExportByName(null, 'kill'), 'int', ['int', 'int']);
|
||||
const kill = new NativeFunction(Module.getGlobalExportByName('kill'), 'int', ['int', 'int']);
|
||||
for (const pid of suspendedPids) {
|
||||
kill(pid, SIGKILL);
|
||||
}
|
||||
@@ -49,7 +49,7 @@ rpc.exports = {
|
||||
|
||||
applyJailbreakQuirks();
|
||||
|
||||
Interceptor.attach(Module.getExportByName('/usr/lib/system/libsystem_kernel.dylib', '__posix_spawn'), {
|
||||
Interceptor.attach(Process.getModuleByName('/usr/lib/system/libsystem_kernel.dylib').getExportByName('__posix_spawn'), {
|
||||
onEnter(args) {
|
||||
const env = parseStringv(args[4]);
|
||||
const prewarm = isPrewarmLaunch(env);
|
||||
@@ -238,7 +238,7 @@ function instrumentInserter(at) {
|
||||
}
|
||||
|
||||
function findJbdCallImpl() {
|
||||
const impl = Module.findExportByName(null, 'jbd_call');
|
||||
const impl = Module.findGlobalExportByName('jbd_call');
|
||||
if (impl !== null)
|
||||
return impl;
|
||||
|
||||
@@ -257,7 +257,7 @@ function findSubstrateLauncher() {
|
||||
if (Process.arch !== 'arm64')
|
||||
return null;
|
||||
|
||||
const imp = Module.enumerateImports('/sbin/launchd').filter(imp => imp.name === 'posix_spawn')[0];
|
||||
const imp = Process.mainModule.enumerateImports().find(imp => imp.name === 'posix_spawn');
|
||||
if (imp === undefined)
|
||||
return null;
|
||||
const impl = imp.slot.readPointer().strip();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import ObjC from 'frida-objc-bridge';
|
||||
|
||||
const YES = ptr(1);
|
||||
|
||||
const {
|
||||
@@ -53,7 +55,8 @@ function tryInitialize() {
|
||||
}
|
||||
|
||||
if (!tryInitialize()) {
|
||||
const listener = Interceptor.attach(Module.getExportByName('/usr/lib/system/libsystem_trace.dylib', 'os_log_type_enabled'), () => {
|
||||
const listener = Interceptor.attach(
|
||||
Process.getModuleByName('/usr/lib/system/libsystem_trace.dylib').getExportByName('os_log_type_enabled'), () => {
|
||||
tryInitialize();
|
||||
listener.detach();
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
osanalytics_js = custom_target('osanalytics.js',
|
||||
input: [
|
||||
'package.json',
|
||||
'package-lock.json',
|
||||
'index.js',
|
||||
],
|
||||
output: [
|
||||
'osanalytics.js',
|
||||
],
|
||||
command: [
|
||||
python,
|
||||
compile_js_agent,
|
||||
'@OUTPUT@',
|
||||
'@PRIVATE_DIR@',
|
||||
npm,
|
||||
'@INPUT@',
|
||||
],
|
||||
)
|
||||
+490
@@ -0,0 +1,490 @@
|
||||
{
|
||||
"name": "frida-osanalytics-agent",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "frida-osanalytics-agent",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"frida-objc-bridge": "^8.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.25.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
|
||||
"integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
|
||||
"integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
|
||||
"integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
|
||||
"integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
|
||||
"integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.25.5",
|
||||
"@esbuild/android-arm": "0.25.5",
|
||||
"@esbuild/android-arm64": "0.25.5",
|
||||
"@esbuild/android-x64": "0.25.5",
|
||||
"@esbuild/darwin-arm64": "0.25.5",
|
||||
"@esbuild/darwin-x64": "0.25.5",
|
||||
"@esbuild/freebsd-arm64": "0.25.5",
|
||||
"@esbuild/freebsd-x64": "0.25.5",
|
||||
"@esbuild/linux-arm": "0.25.5",
|
||||
"@esbuild/linux-arm64": "0.25.5",
|
||||
"@esbuild/linux-ia32": "0.25.5",
|
||||
"@esbuild/linux-loong64": "0.25.5",
|
||||
"@esbuild/linux-mips64el": "0.25.5",
|
||||
"@esbuild/linux-ppc64": "0.25.5",
|
||||
"@esbuild/linux-riscv64": "0.25.5",
|
||||
"@esbuild/linux-s390x": "0.25.5",
|
||||
"@esbuild/linux-x64": "0.25.5",
|
||||
"@esbuild/netbsd-arm64": "0.25.5",
|
||||
"@esbuild/netbsd-x64": "0.25.5",
|
||||
"@esbuild/openbsd-arm64": "0.25.5",
|
||||
"@esbuild/openbsd-x64": "0.25.5",
|
||||
"@esbuild/sunos-x64": "0.25.5",
|
||||
"@esbuild/win32-arm64": "0.25.5",
|
||||
"@esbuild/win32-ia32": "0.25.5",
|
||||
"@esbuild/win32-x64": "0.25.5"
|
||||
}
|
||||
},
|
||||
"node_modules/frida-objc-bridge": {
|
||||
"version": "8.0.5",
|
||||
"resolved": "https://registry.npmjs.org/frida-objc-bridge/-/frida-objc-bridge-8.0.5.tgz",
|
||||
"integrity": "sha512-0xnL0VgxSQXgLj3S33S0kdAvLrCHSCIZb9HVraEgEyKnf9wOcu0KZ9fI5xzx0e0y+ry/g2Vl3yw9j3dsn92Ffg==",
|
||||
"license": "LGPL-2.0 WITH WxWindows-exception-3.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "frida-osanalytics-agent",
|
||||
"version": "1.0.0",
|
||||
"description": "Agent used by Frida's Darwin backend on iOS/iPadOS/tvOS",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "esbuild index.js --outfile=osanalytics.js --bundle --platform=node --target=es2022 --minify"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.25.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"frida-objc-bridge": "^8.0.5"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
const LIBSYSTEM_KERNEL_PATH = '/usr/lib/system/libsystem_kernel.dylib';
|
||||
const CORESYMBOLICATION_PATH = '/System/Library/PrivateFrameworks/CoreSymbolication.framework/CoreSymbolication';
|
||||
const CRASH_REPORTER_SUPPORT_PATH = '/System/Library/PrivateFrameworks/CrashReporterSupport.framework/CrashReporterSupport';
|
||||
import ObjC from 'frida-objc-bridge';
|
||||
|
||||
const YES = ptr(1);
|
||||
|
||||
const CSTypeRef = ['pointer', 'pointer'];
|
||||
@@ -17,42 +16,45 @@ const complexFuncOptions = {
|
||||
scheduling: 'cooperative',
|
||||
exceptions: 'propagate'
|
||||
};
|
||||
const libsystemKernel = Process.getModuleByName('/usr/lib/system/libsystem_kernel.dylib');
|
||||
const _pidForTask = new NativeFunction(
|
||||
Module.getExportByName(LIBSYSTEM_KERNEL_PATH, 'pid_for_task'),
|
||||
libsystemKernel.getExportByName('pid_for_task'),
|
||||
'int',
|
||||
['uint', 'pointer'],
|
||||
simpleFuncOptions
|
||||
);
|
||||
const unlink = new NativeFunction(
|
||||
Module.getExportByName(LIBSYSTEM_KERNEL_PATH, 'unlink'),
|
||||
libsystemKernel.getExportByName('unlink'),
|
||||
'int',
|
||||
['pointer'],
|
||||
simpleFuncOptions
|
||||
);
|
||||
const coreSymbolication = Process.getModuleByName('/System/Library/PrivateFrameworks/CoreSymbolication.framework/CoreSymbolication');
|
||||
const CSSymbolicatorGetSymbolWithAddressAtTime = new NativeFunction(
|
||||
Module.getExportByName(CORESYMBOLICATION_PATH, 'CSSymbolicatorGetSymbolWithAddressAtTime'),
|
||||
coreSymbolication.getExportByName('CSSymbolicatorGetSymbolWithAddressAtTime'),
|
||||
CSTypeRef,
|
||||
[CSTypeRef, 'uint64', 'uint64'],
|
||||
complexFuncOptions
|
||||
);
|
||||
const CSIsNull = new NativeFunction(
|
||||
Module.getExportByName(CORESYMBOLICATION_PATH, 'CSIsNull'),
|
||||
coreSymbolication.getExportByName('CSIsNull'),
|
||||
'int',
|
||||
[CSTypeRef],
|
||||
simpleFuncOptions
|
||||
);
|
||||
const mappedMemoryRead = new NativeFunction(
|
||||
Module.getExportByName(CORESYMBOLICATION_PATH, 'mapped_memory_read'),
|
||||
coreSymbolication.getExportByName('mapped_memory_read'),
|
||||
'uint',
|
||||
['pointer', 'uint64', 'uint64', 'pointer'],
|
||||
simpleFuncOptions
|
||||
);
|
||||
const mappedMemoryReadPointer = new NativeFunction(
|
||||
Module.getExportByName(CORESYMBOLICATION_PATH, 'mapped_memory_read_pointer'),
|
||||
coreSymbolication.getExportByName('mapped_memory_read_pointer'),
|
||||
'uint',
|
||||
['pointer', 'uint64', 'pointer'],
|
||||
simpleFuncOptions
|
||||
);
|
||||
const crashReporterSupport = Process.getModuleByName('/System/Library/PrivateFrameworks/CrashReporterSupport.framework/CrashReporterSupport');
|
||||
|
||||
const {
|
||||
AppleErrorReport,
|
||||
@@ -67,7 +69,7 @@ const sessions = new Map();
|
||||
let osaHookState = 'pending';
|
||||
|
||||
function initialize() {
|
||||
const listener = Interceptor.attach(Module.getExportByName(CRASH_REPORTER_SUPPORT_PATH, 'CRCreateDirectoryStructure'), () => {
|
||||
const listener = Interceptor.attach(crashReporterSupport.getExportByName('CRCreateDirectoryStructure'), () => {
|
||||
applyInstrumentation();
|
||||
listener.detach();
|
||||
});
|
||||
@@ -144,7 +146,7 @@ function applyInstrumentation() {
|
||||
},
|
||||
});
|
||||
|
||||
Interceptor.attach(Module.getExportByName(CORESYMBOLICATION_PATH, 'task_is_64bit'), {
|
||||
Interceptor.attach(coreSymbolication.getExportByName('task_is_64bit'), {
|
||||
onEnter(args) {
|
||||
this.pid = pidForTask(args[0].toUInt32());
|
||||
},
|
||||
@@ -216,7 +218,7 @@ function applyInstrumentation() {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Interceptor.attach(Module.getExportByName(LIBSYSTEM_KERNEL_PATH, 'rename'), {
|
||||
Interceptor.attach(libsystemKernel.getExportByName('rename'), {
|
||||
onEnter(args) {
|
||||
const newPath = args[1].readUtf8String();
|
||||
const session = getSession(this.threadId, 'rename');
|
||||
@@ -225,7 +227,7 @@ function applyInstrumentation() {
|
||||
},
|
||||
});
|
||||
|
||||
Interceptor.attach(Module.getExportByName(LIBSYSTEM_KERNEL_PATH, 'open_dprotected_np'), {
|
||||
Interceptor.attach(libsystemKernel.getExportByName('open_dprotected_np'), {
|
||||
onEnter(args) {
|
||||
const path = args[0].readUtf8String();
|
||||
this.isCrashLog = /\.ips$/.test(path);
|
||||
@@ -237,7 +239,7 @@ function applyInstrumentation() {
|
||||
},
|
||||
});
|
||||
|
||||
Interceptor.attach(Module.getExportByName(LIBSYSTEM_KERNEL_PATH, 'write'), {
|
||||
Interceptor.attach(libsystemKernel.getExportByName('write'), {
|
||||
onEnter(args) {
|
||||
const fd = args[0].toInt32();
|
||||
this.buf = args[1];
|
||||
@@ -266,7 +268,7 @@ function applyInstrumentation() {
|
||||
});
|
||||
}
|
||||
|
||||
Interceptor.attach(Module.getExportByName(CRASH_REPORTER_SUPPORT_PATH, 'OSAPreferencesGetBoolValue'), {
|
||||
Interceptor.attach(crashReporterSupport.getExportByName('OSAPreferencesGetBoolValue'), {
|
||||
onEnter(args) {
|
||||
this.name = new ObjC.Object(args[0]).toString();
|
||||
this.domain = new ObjC.Object(args[1]).toString();
|
||||
@@ -0,0 +1,18 @@
|
||||
reportcrash_js = custom_target('reportcrash.js',
|
||||
input: [
|
||||
'package.json',
|
||||
'package-lock.json',
|
||||
'index.js',
|
||||
],
|
||||
output: [
|
||||
'reportcrash.js',
|
||||
],
|
||||
command: [
|
||||
python,
|
||||
compile_js_agent,
|
||||
'@OUTPUT@',
|
||||
'@PRIVATE_DIR@',
|
||||
npm,
|
||||
'@INPUT@',
|
||||
],
|
||||
)
|
||||
+490
@@ -0,0 +1,490 @@
|
||||
{
|
||||
"name": "frida-reportcrash-agent",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "frida-reportcrash-agent",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"frida-objc-bridge": "^8.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.25.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
|
||||
"integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
|
||||
"integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
|
||||
"integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
|
||||
"integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
|
||||
"integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.25.5",
|
||||
"@esbuild/android-arm": "0.25.5",
|
||||
"@esbuild/android-arm64": "0.25.5",
|
||||
"@esbuild/android-x64": "0.25.5",
|
||||
"@esbuild/darwin-arm64": "0.25.5",
|
||||
"@esbuild/darwin-x64": "0.25.5",
|
||||
"@esbuild/freebsd-arm64": "0.25.5",
|
||||
"@esbuild/freebsd-x64": "0.25.5",
|
||||
"@esbuild/linux-arm": "0.25.5",
|
||||
"@esbuild/linux-arm64": "0.25.5",
|
||||
"@esbuild/linux-ia32": "0.25.5",
|
||||
"@esbuild/linux-loong64": "0.25.5",
|
||||
"@esbuild/linux-mips64el": "0.25.5",
|
||||
"@esbuild/linux-ppc64": "0.25.5",
|
||||
"@esbuild/linux-riscv64": "0.25.5",
|
||||
"@esbuild/linux-s390x": "0.25.5",
|
||||
"@esbuild/linux-x64": "0.25.5",
|
||||
"@esbuild/netbsd-arm64": "0.25.5",
|
||||
"@esbuild/netbsd-x64": "0.25.5",
|
||||
"@esbuild/openbsd-arm64": "0.25.5",
|
||||
"@esbuild/openbsd-x64": "0.25.5",
|
||||
"@esbuild/sunos-x64": "0.25.5",
|
||||
"@esbuild/win32-arm64": "0.25.5",
|
||||
"@esbuild/win32-ia32": "0.25.5",
|
||||
"@esbuild/win32-x64": "0.25.5"
|
||||
}
|
||||
},
|
||||
"node_modules/frida-objc-bridge": {
|
||||
"version": "8.0.5",
|
||||
"resolved": "https://registry.npmjs.org/frida-objc-bridge/-/frida-objc-bridge-8.0.5.tgz",
|
||||
"integrity": "sha512-0xnL0VgxSQXgLj3S33S0kdAvLrCHSCIZb9HVraEgEyKnf9wOcu0KZ9fI5xzx0e0y+ry/g2Vl3yw9j3dsn92Ffg==",
|
||||
"license": "LGPL-2.0 WITH WxWindows-exception-3.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "frida-reportcrash-agent",
|
||||
"version": "1.0.0",
|
||||
"description": "Agent used by Frida's Darwin backend on iOS/iPadOS/tvOS",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "esbuild index.js --outfile=reportcrash.js --bundle --platform=node --target=es2022 --minify"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.25.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"frida-objc-bridge": "^8.0.5"
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ const POSIX_SPAWN_START_SUSPENDED = 0x0080;
|
||||
|
||||
applyJailbreakQuirks();
|
||||
|
||||
Interceptor.attach(Module.getExportByName('/usr/lib/system/libsystem_kernel.dylib', '__posix_spawn'), {
|
||||
Interceptor.attach(Process.getModuleByName('/usr/lib/system/libsystem_kernel.dylib').getExportByName('__posix_spawn'), {
|
||||
onEnter(args) {
|
||||
const attrs = args[2].add(Process.pointerSize).readPointer();
|
||||
|
||||
@@ -42,7 +42,7 @@ function sabotageJbdCall(jbdCallImpl) {
|
||||
}
|
||||
|
||||
function instrumentSubstrateBootstrapper(bootstrapper) {
|
||||
Interceptor.attach(Module.getExportByName('/usr/lib/system/libdyld.dylib', 'dlopen'), {
|
||||
Interceptor.attach(Process.getModuleByName('/usr/lib/system/libdyld.dylib').getExportByName('dlopen'), {
|
||||
onEnter(args) {
|
||||
this.path = args[0].readUtf8String();
|
||||
},
|
||||
@@ -66,7 +66,7 @@ function instrumentSubstrateExec(exec) {
|
||||
}
|
||||
|
||||
function findJbdCallImpl() {
|
||||
const impl = Module.findExportByName(null, 'jbd_call');
|
||||
const impl = Module.findGlobalExportByName('jbd_call');
|
||||
if (impl !== null)
|
||||
return impl;
|
||||
|
||||
|
||||
@@ -2027,7 +2027,9 @@ _frida_darwin_helper_backend_prepare_spawn_instance_for_injection (FridaDarwinHe
|
||||
}
|
||||
}
|
||||
|
||||
modern_entry_address = gum_darwin_module_resolve_symbol_address (dyld, "__ZN5dyld44APIs19_libdyld_initializeEPKNS_16LibSystemHelpersE");
|
||||
modern_entry_address = gum_darwin_module_resolve_symbol_address (dyld, "__ZN5dyld44APIs19_libdyld_initializeEv");
|
||||
if (modern_entry_address == 0)
|
||||
modern_entry_address = gum_darwin_module_resolve_symbol_address (dyld, "__ZN5dyld44APIs19_libdyld_initializeEPKNS_16LibSystemHelpersE");
|
||||
instance->dyld_flavor = (modern_entry_address != 0) ? FRIDA_DYLD_V4_PLUS : FRIDA_DYLD_V3_MINUS;
|
||||
if (instance->dyld_flavor == FRIDA_DYLD_V4_PLUS)
|
||||
{
|
||||
@@ -2113,7 +2115,12 @@ _frida_darwin_helper_backend_prepare_spawn_instance_for_injection (FridaDarwinHe
|
||||
if (instance->dyld_flavor == FRIDA_DYLD_V4_PLUS)
|
||||
{
|
||||
GumAddress restart_with_dyld_in_cache = gum_darwin_module_resolve_symbol_address (dyld,
|
||||
"__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPK15DyldSharedCachePv");
|
||||
"__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN6mach_o6HeaderEPK15DyldSharedCachePv");
|
||||
if (restart_with_dyld_in_cache == 0)
|
||||
{
|
||||
restart_with_dyld_in_cache = gum_darwin_module_resolve_symbol_address (dyld,
|
||||
"__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPK15DyldSharedCachePv");
|
||||
}
|
||||
if (restart_with_dyld_in_cache == 0)
|
||||
{
|
||||
restart_with_dyld_in_cache = gum_darwin_module_resolve_symbol_address (dyld,
|
||||
|
||||
@@ -109,6 +109,8 @@ _frida_get_springboard_api (void)
|
||||
gum_interceptor_replace (interceptor, frida_xpc_look_up_endpoint, frida_replacement_xpc_look_up_endpoint, NULL, NULL);
|
||||
else
|
||||
g_error ("Unable to locate _xpc_look_up_endpoint(); please file a bug");
|
||||
|
||||
g_object_unref (interceptor);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
+8
-12
@@ -360,12 +360,7 @@ namespace Frida {
|
||||
|
||||
start_request.resolve (true);
|
||||
} catch (IOError e) {
|
||||
service.provider_available.disconnect (on_provider_available);
|
||||
service.provider_unavailable.disconnect (on_provider_unavailable);
|
||||
service = null;
|
||||
|
||||
start_request.reject (e);
|
||||
start_request = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1464,7 +1459,9 @@ namespace Frida {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert (session_id != null);
|
||||
if (session_id == null)
|
||||
return;
|
||||
|
||||
agent_sessions.unset (session_id);
|
||||
|
||||
if (may_block) {
|
||||
@@ -1488,6 +1485,8 @@ namespace Frida {
|
||||
Promise<bool> detach_request;
|
||||
if (pending_detach_requests.unset (id, out detach_request))
|
||||
detach_request.resolve (true);
|
||||
else if (session != null)
|
||||
agent_sessions.unset (id);
|
||||
}
|
||||
|
||||
private void on_spawn_added (HostSpawnInfo info) {
|
||||
@@ -1867,18 +1866,15 @@ namespace Frida {
|
||||
public signal void detached ();
|
||||
public signal void message (string json, Bytes? data);
|
||||
|
||||
public weak Device device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
private weak Device device;
|
||||
|
||||
private Promise<BusSession>? attach_request;
|
||||
|
||||
private BusSession? active_session;
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
internal Bus (Device device) {
|
||||
Object (device: device);
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
public bool is_detached () {
|
||||
|
||||
@@ -200,6 +200,8 @@ namespace Frida.Fruity {
|
||||
namespace Network {
|
||||
public async void wait_until_interfaces_ready (Gee.Collection<string> interface_names, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var wait_cancellable = new Cancellable ();
|
||||
|
||||
try {
|
||||
var connection = yield GLib.Bus.get (BusType.SYSTEM, cancellable);
|
||||
|
||||
@@ -215,6 +217,22 @@ namespace Frida.Fruity {
|
||||
return;
|
||||
}
|
||||
|
||||
var main_context = MainContext.get_thread_default ();
|
||||
|
||||
var timeout_source = new TimeoutSource.seconds (5);
|
||||
timeout_source.set_callback (() => {
|
||||
wait_cancellable.cancel ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
timeout_source.attach (main_context);
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (() => {
|
||||
wait_cancellable.cancel ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
cancel_source.attach (main_context);
|
||||
|
||||
var remaining = interface_names.size + 1;
|
||||
|
||||
NotifyCompleteFunc on_complete = () => {
|
||||
@@ -225,10 +243,10 @@ namespace Frida.Fruity {
|
||||
|
||||
foreach (var name in interface_names) {
|
||||
if (nm != null) {
|
||||
NetworkManager.wait_until_interface_ready.begin (name, nm, connection, cancellable,
|
||||
NetworkManager.wait_until_interface_ready.begin (name, nm, connection, wait_cancellable,
|
||||
on_complete);
|
||||
} else {
|
||||
Networkd.wait_until_interface_ready.begin (name, netd, connection, cancellable,
|
||||
Networkd.wait_until_interface_ready.begin (name, netd, connection, wait_cancellable,
|
||||
on_complete);
|
||||
}
|
||||
}
|
||||
@@ -238,11 +256,17 @@ namespace Frida.Fruity {
|
||||
on_complete ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (MainContext.get_thread_default ());
|
||||
source.attach (main_context);
|
||||
|
||||
yield;
|
||||
|
||||
cancel_source.destroy ();
|
||||
timeout_source.destroy ();
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
|
||||
if (wait_cancellable.is_cancelled ())
|
||||
throw new Error.TIMED_OUT ("Unexpectedly timed out while waiting for interfaces to become ready");
|
||||
}
|
||||
|
||||
private async bool system_has_service (string name, DBusConnection connection, Cancellable? cancellable) throws GLib.Error {
|
||||
@@ -276,16 +300,25 @@ namespace Frida.Fruity {
|
||||
wait_until_interface_ready.callback ();
|
||||
});
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (wait_until_interface_ready.callback);
|
||||
cancel_source.attach (MainContext.get_thread_default ());
|
||||
|
||||
while (!cancellable.is_cancelled ()) {
|
||||
uint32 state, reason;
|
||||
device_proxy.get_cached_property ("StateReason").get ("(uu)", out state, out reason);
|
||||
if (state == DEVICE_STATE_ACTIVATED)
|
||||
break;
|
||||
if (state == DEVICE_STATE_DISCONNECTED && reason != DEVICE_STATE_REASON_NONE)
|
||||
if (state == DEVICE_STATE_DISCONNECTED &&
|
||||
reason != DEVICE_STATE_REASON_NONE &&
|
||||
reason != DEVICE_STATE_REASON_CARRIER) {
|
||||
break;
|
||||
}
|
||||
yield;
|
||||
}
|
||||
|
||||
cancel_source.destroy ();
|
||||
|
||||
device_proxy.disconnect (handler);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
@@ -309,6 +342,7 @@ namespace Frida.Fruity {
|
||||
private const uint32 DEVICE_STATE_ACTIVATED = 100;
|
||||
|
||||
private const uint32 DEVICE_STATE_REASON_NONE = 0;
|
||||
private const uint32 DEVICE_STATE_REASON_CARRIER = 40;
|
||||
}
|
||||
|
||||
namespace Networkd {
|
||||
@@ -327,6 +361,10 @@ namespace Frida.Fruity {
|
||||
wait_until_interface_ready.callback ();
|
||||
});
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (wait_until_interface_ready.callback);
|
||||
cancel_source.attach (MainContext.get_thread_default ());
|
||||
|
||||
while (!cancellable.is_cancelled ()) {
|
||||
string operational_state;
|
||||
link_proxy.get_cached_property ("OperationalState").get ("s", out operational_state);
|
||||
@@ -335,6 +373,8 @@ namespace Frida.Fruity {
|
||||
yield;
|
||||
}
|
||||
|
||||
cancel_source.destroy ();
|
||||
|
||||
link_proxy.disconnect (handler);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
|
||||
@@ -129,6 +129,7 @@ namespace Frida.Fruity {
|
||||
private ConnectionType _connection_type;
|
||||
private string _udid;
|
||||
private string _name;
|
||||
private string _auth_state;
|
||||
private Bytes? _remote_unlock_host_key;
|
||||
|
||||
private Promise<Tunnel>? tunnel_request;
|
||||
@@ -155,6 +156,11 @@ namespace Frida.Fruity {
|
||||
_name = reader.read_member ("name").get_string_value ();
|
||||
reader.end_member ();
|
||||
|
||||
_auth_state = reader.read_member ("authState").read_member ("rawCase").get_string_value ();
|
||||
reader
|
||||
.end_member ()
|
||||
.end_member ();
|
||||
|
||||
if (reader.has_member ("remoteUnlockHostKey")) {
|
||||
_remote_unlock_host_key = new Bytes (reader.read_member ("remoteUnlockHostKey").get_data_value ());
|
||||
reader.end_member ();
|
||||
@@ -232,6 +238,8 @@ namespace Frida.Fruity {
|
||||
tunnel_request = new Promise<Tunnel> ();
|
||||
|
||||
try {
|
||||
yield ensure_paired (cancellable);
|
||||
|
||||
var tunnel = new MacOSTunnel (pairing_device, _remote_unlock_host_key);
|
||||
yield tunnel.attach (cancellable);
|
||||
|
||||
@@ -245,6 +253,17 @@ namespace Frida.Fruity {
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ensure_paired (Cancellable? cancellable) throws Error, IOError {
|
||||
if (_auth_state == "authenticated")
|
||||
return;
|
||||
|
||||
var r = new PairingdRequest ("RemotePairing.InitiatePairingCommand");
|
||||
r.body.set_bool ("requireNonInteractive", false);
|
||||
var response = yield pairing_device.request (r.message, cancellable);
|
||||
var reader = new XpcObjectReader (response);
|
||||
reader.check_nserror (() => Error.PERMISSION_DENIED);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class MacOSTunnel : Object, Tunnel {
|
||||
@@ -301,6 +320,7 @@ namespace Frida.Fruity {
|
||||
_opened_at = get_monotonic_time ();
|
||||
|
||||
var reader = new XpcObjectReader (response);
|
||||
reader.check_nserror (() => Error.NOT_SUPPORTED);
|
||||
reader.read_member ("response");
|
||||
|
||||
_assertion_identifier = (Darwin.Xpc.Uuid) reader
|
||||
|
||||
+279
-87
@@ -8,6 +8,8 @@ namespace Frida.Fruity {
|
||||
private Gee.List<Backend> backends = new Gee.ArrayList<Backend> ();
|
||||
private Gee.Map<string, Device> devices = new Gee.HashMap<string, Device> ();
|
||||
|
||||
private PairingStore pairing_store = new PairingStore ();
|
||||
|
||||
private enum State {
|
||||
CREATED,
|
||||
STARTING,
|
||||
@@ -18,11 +20,11 @@ namespace Frida.Fruity {
|
||||
private delegate void NotifyCompleteFunc ();
|
||||
|
||||
construct {
|
||||
add_backend (new UsbmuxBackend ());
|
||||
add_backend (new UsbmuxBackend (pairing_store));
|
||||
#if MACOS
|
||||
add_backend (new MacOSCoreDeviceBackend ());
|
||||
#else
|
||||
add_backend (new PortableCoreDeviceBackend ());
|
||||
add_backend (new PortableCoreDeviceBackend (pairing_store));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -184,10 +186,6 @@ namespace Frida.Fruity {
|
||||
default = new Gee.TreeSet<Transport> (compare_transports);
|
||||
}
|
||||
|
||||
private Gee.Queue<UsbmuxLockdownServiceRequest> usbmux_lockdown_service_requests =
|
||||
new Gee.ArrayQueue<UsbmuxLockdownServiceRequest> ();
|
||||
private LockdownClient? cached_usbmux_lockdown_client;
|
||||
|
||||
private const string[] LOCKDOWN_SERVICES_WITHOUT_ESCROW_BAG_SUPPORT = {
|
||||
"com.apple.accessibility.axAuditDaemon.remoteserver",
|
||||
"com.apple.afc",
|
||||
@@ -205,26 +203,30 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public UsbmuxDevice? find_usbmux_device () {
|
||||
var transport = transports.first_match (t => t.usbmux_device != null && t.connection_type == USB);
|
||||
if (transport == null)
|
||||
transport = transports.first_match (t => t.usbmux_device != null);
|
||||
return (transport != null) ? transport.usbmux_device : null;
|
||||
var t = transports.first_match (t => t.usbmux_device != null);
|
||||
return (t != null) ? t.usbmux_device : null;
|
||||
}
|
||||
|
||||
public UsbmuxDevice get_usbmux_device () throws Error {
|
||||
var d = find_usbmux_device ();
|
||||
if (d == null)
|
||||
private UsbmuxTransport get_usbmux_transport () throws Error {
|
||||
var t = transports.first_match (t => t is UsbmuxTransport);
|
||||
if (t == null)
|
||||
throw new Error.NOT_SUPPORTED ("USB connection not available");
|
||||
return d;
|
||||
return (UsbmuxTransport) t;
|
||||
}
|
||||
|
||||
public async Tunnel? find_tunnel (Cancellable? cancellable) throws Error, IOError {
|
||||
var usbmux_device = find_usbmux_device ();
|
||||
foreach (var transport in transports) {
|
||||
|
||||
var transports_to_try = new Gee.ArrayList<Transport> ();
|
||||
transports_to_try.add_all_iterator (transports.filter (t => t.usbmux_device == null));
|
||||
transports_to_try.add_all_iterator (transports.filter (t => t.usbmux_device != null));
|
||||
|
||||
foreach (var transport in transports_to_try) {
|
||||
Tunnel? tunnel = yield transport.find_tunnel (usbmux_device, cancellable);
|
||||
if (tunnel != null)
|
||||
return tunnel;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -283,19 +285,7 @@ namespace Frida.Fruity {
|
||||
return stream;
|
||||
}
|
||||
|
||||
if (service_name == "") {
|
||||
var client = yield open_usbmux_lockdown_client (cancellable);
|
||||
return client.service.stream;
|
||||
}
|
||||
|
||||
var request = new UsbmuxLockdownServiceRequest (service_name, cancellable);
|
||||
bool first_request = usbmux_lockdown_service_requests.is_empty;
|
||||
usbmux_lockdown_service_requests.offer (request);
|
||||
|
||||
if (first_request)
|
||||
process_usbmux_lockdown_service_requests.begin ();
|
||||
|
||||
return yield request.promise.future.wait_async (cancellable);
|
||||
return yield get_usbmux_transport ().open_lockdown_service (service_name, cancellable);
|
||||
}
|
||||
|
||||
// FIXME: Replace with `element in array`-check once Vala compiler bug has been fixed so generated C code is warning-free.
|
||||
@@ -307,41 +297,6 @@ namespace Frida.Fruity {
|
||||
return true;
|
||||
}
|
||||
|
||||
private async void process_usbmux_lockdown_service_requests () {
|
||||
UsbmuxLockdownServiceRequest? req;
|
||||
bool already_invalidated = false;
|
||||
while ((req = usbmux_lockdown_service_requests.peek ()) != null) {
|
||||
try {
|
||||
if (cached_usbmux_lockdown_client == null)
|
||||
cached_usbmux_lockdown_client = yield open_usbmux_lockdown_client (req.cancellable);
|
||||
var stream = yield cached_usbmux_lockdown_client.start_service (req.service_name, req.cancellable);
|
||||
req.promise.resolve (stream);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is LockdownError.CONNECTION_CLOSED && cached_usbmux_lockdown_client != null &&
|
||||
!already_invalidated) {
|
||||
cached_usbmux_lockdown_client = null;
|
||||
already_invalidated = true;
|
||||
continue;
|
||||
}
|
||||
req.promise.reject ((e is LockdownError.INVALID_SERVICE)
|
||||
? (Error) new Error.NOT_SUPPORTED ("%s", e.message)
|
||||
: (Error) new Error.TRANSPORT ("%s", e.message));
|
||||
}
|
||||
|
||||
usbmux_lockdown_service_requests.poll ();
|
||||
}
|
||||
}
|
||||
|
||||
private async LockdownClient open_usbmux_lockdown_client (Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
var client = yield LockdownClient.open (get_usbmux_device (), cancellable);
|
||||
yield client.start_session (cancellable);
|
||||
return client;
|
||||
} catch (LockdownError e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async IOStream open_channel (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
string[] tokens = address.split (":", 2);
|
||||
unowned string protocol = tokens[0];
|
||||
@@ -452,17 +407,6 @@ namespace Frida.Fruity {
|
||||
score++;
|
||||
return score;
|
||||
}
|
||||
|
||||
private class UsbmuxLockdownServiceRequest {
|
||||
public string service_name;
|
||||
public Cancellable? cancellable;
|
||||
public Promise<IOStream> promise = new Promise<IOStream> ();
|
||||
|
||||
public UsbmuxLockdownServiceRequest (string service_name, Cancellable? cancellable) {
|
||||
this.service_name = service_name;
|
||||
this.cancellable = cancellable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TcpChannel {
|
||||
@@ -537,6 +481,11 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private sealed class UsbmuxBackend : Object, Backend {
|
||||
public PairingStore pairing_store {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public bool available {
|
||||
get {
|
||||
return usbmux != null;
|
||||
@@ -553,6 +502,10 @@ namespace Frida.Fruity {
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
public UsbmuxBackend (PairingStore pairing_store) {
|
||||
Object (pairing_store: pairing_store);
|
||||
}
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
start_request = new Promise<bool> ();
|
||||
start_cancellable = new Cancellable ();
|
||||
@@ -669,7 +622,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private async void add_transport (UsbmuxDevice device) {
|
||||
var transport = new UsbmuxTransport (device);
|
||||
var transport = new UsbmuxTransport (device, pairing_store);
|
||||
transports[device] = transport;
|
||||
|
||||
string? name = null;
|
||||
@@ -735,6 +688,11 @@ namespace Frida.Fruity {
|
||||
construct;
|
||||
}
|
||||
|
||||
public PairingStore pairing_store {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public ConnectionType connection_type {
|
||||
get {
|
||||
return device.connection_type;
|
||||
@@ -768,16 +726,218 @@ namespace Frida.Fruity {
|
||||
internal string _name;
|
||||
internal Variant? _icon;
|
||||
|
||||
public UsbmuxTransport (UsbmuxDevice device) {
|
||||
Object (device: device);
|
||||
private Promise<Tunnel?>? tunnel_request;
|
||||
|
||||
private Gee.Queue<UsbmuxLockdownServiceRequest> lockdown_service_requests =
|
||||
new Gee.ArrayQueue<UsbmuxLockdownServiceRequest> ();
|
||||
private LockdownClient? cached_lockdown_client;
|
||||
|
||||
public UsbmuxTransport (UsbmuxDevice device, PairingStore store) {
|
||||
Object (device: device, pairing_store: store);
|
||||
}
|
||||
|
||||
public async Tunnel? find_tunnel (UsbmuxDevice? device, Cancellable? cancellable) throws Error, IOError {
|
||||
return null;
|
||||
while (tunnel_request != null) {
|
||||
try {
|
||||
return yield tunnel_request.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
throw e;
|
||||
} catch (IOError e) {
|
||||
cancellable.set_error_if_cancelled ();
|
||||
}
|
||||
}
|
||||
tunnel_request = new Promise<Tunnel> ();
|
||||
|
||||
try {
|
||||
IOStream? stream = null;
|
||||
try {
|
||||
stream = yield open_lockdown_service ("com.apple.internal.devicecompute.CoreDeviceProxy", cancellable);
|
||||
} catch (Error e) {
|
||||
if (!(e is Error.NOT_SUPPORTED))
|
||||
throw e;
|
||||
}
|
||||
|
||||
UsbmuxTunnel? tunnel = null;
|
||||
if (stream != null) {
|
||||
tunnel = new UsbmuxTunnel (stream, pairing_store);
|
||||
tunnel.lost.connect (on_tunnel_lost);
|
||||
try {
|
||||
yield tunnel.open (cancellable);
|
||||
} catch (Error e) {
|
||||
if (e is Error.NOT_SUPPORTED)
|
||||
tunnel = null;
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
tunnel_request.resolve (tunnel);
|
||||
|
||||
return tunnel;
|
||||
} catch (GLib.Error e) {
|
||||
tunnel_request.reject (e);
|
||||
tunnel_request = null;
|
||||
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
|
||||
private void on_tunnel_lost () {
|
||||
tunnel_request = null;
|
||||
}
|
||||
|
||||
public async IOStream open_lockdown_service (string service_name, Cancellable? cancellable) throws Error, IOError {
|
||||
if (service_name == "") {
|
||||
var client = yield open_usbmux_lockdown_client (cancellable);
|
||||
return client.service.stream;
|
||||
}
|
||||
|
||||
var request = new UsbmuxLockdownServiceRequest (service_name, cancellable);
|
||||
bool first_request = lockdown_service_requests.is_empty;
|
||||
lockdown_service_requests.offer (request);
|
||||
|
||||
if (first_request)
|
||||
process_lockdown_service_requests.begin ();
|
||||
|
||||
return yield request.promise.future.wait_async (cancellable);
|
||||
}
|
||||
|
||||
private async void process_lockdown_service_requests () {
|
||||
UsbmuxLockdownServiceRequest? req;
|
||||
bool already_invalidated = false;
|
||||
while ((req = lockdown_service_requests.peek ()) != null) {
|
||||
try {
|
||||
if (cached_lockdown_client == null)
|
||||
cached_lockdown_client = yield open_usbmux_lockdown_client (req.cancellable);
|
||||
var stream = yield cached_lockdown_client.start_service (req.service_name, req.cancellable);
|
||||
req.promise.resolve (stream);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is LockdownError.CONNECTION_CLOSED && cached_lockdown_client != null &&
|
||||
!already_invalidated) {
|
||||
cached_lockdown_client = null;
|
||||
already_invalidated = true;
|
||||
continue;
|
||||
}
|
||||
req.promise.reject ((e is LockdownError.INVALID_SERVICE)
|
||||
? (Error) new Error.NOT_SUPPORTED ("%s", e.message)
|
||||
: (Error) new Error.TRANSPORT ("%s", e.message));
|
||||
}
|
||||
|
||||
lockdown_service_requests.poll ();
|
||||
}
|
||||
}
|
||||
|
||||
private async LockdownClient open_usbmux_lockdown_client (Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
var client = yield LockdownClient.open (device, cancellable);
|
||||
yield client.start_session (cancellable);
|
||||
return client;
|
||||
} catch (LockdownError e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
private class UsbmuxLockdownServiceRequest {
|
||||
public string service_name;
|
||||
public Cancellable? cancellable;
|
||||
public Promise<IOStream> promise = new Promise<IOStream> ();
|
||||
|
||||
public UsbmuxLockdownServiceRequest (string service_name, Cancellable? cancellable) {
|
||||
this.service_name = service_name;
|
||||
this.cancellable = cancellable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class UsbmuxTunnel : Object, Tunnel {
|
||||
public signal void lost ();
|
||||
|
||||
public IOStream stream {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public PairingStore pairing_store {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public DiscoveryService discovery {
|
||||
get {
|
||||
return _discovery_service;
|
||||
}
|
||||
}
|
||||
|
||||
public int64 opened_at {
|
||||
get {
|
||||
return _opened_at;
|
||||
}
|
||||
}
|
||||
|
||||
public Bytes? remote_unlock_host_key {
|
||||
get {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private UsbNcmDriver? ncm;
|
||||
private TcpTunnelConnection? tunnel_connection;
|
||||
private DiscoveryService? _discovery_service;
|
||||
private int64 _opened_at = -1;
|
||||
|
||||
public UsbmuxTunnel (IOStream stream, PairingStore store) {
|
||||
Object (stream: stream, pairing_store: store);
|
||||
}
|
||||
|
||||
public async void open (Cancellable? cancellable) throws Error, IOError {
|
||||
var tc = yield TcpTunnelConnection.open_stream (stream, cancellable);
|
||||
tunnel_connection = tc;
|
||||
tunnel_connection.closed.connect (on_tunnel_connection_close);
|
||||
|
||||
_opened_at = get_monotonic_time ();
|
||||
|
||||
var rsd_endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: tc.remote_address,
|
||||
port: tc.remote_rsd_port,
|
||||
scope_id: tc.tunnel_netstack.scope_id
|
||||
);
|
||||
var rsd_connection = yield tc.tunnel_netstack.open_tcp_connection (rsd_endpoint, cancellable);
|
||||
var disco = yield DiscoveryService.open (rsd_connection, cancellable);
|
||||
|
||||
tunnel_connection = tc;
|
||||
_discovery_service = disco;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
_discovery_service.close ();
|
||||
|
||||
yield tunnel_connection.close (cancellable);
|
||||
|
||||
if (ncm != null)
|
||||
ncm.close ();
|
||||
}
|
||||
|
||||
public async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError {
|
||||
var netstack = tunnel_connection.tunnel_netstack;
|
||||
var endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: tunnel_connection.remote_address,
|
||||
port: port,
|
||||
scope_id: netstack.scope_id
|
||||
);
|
||||
return yield netstack.open_tcp_connection (endpoint, cancellable);
|
||||
}
|
||||
|
||||
private void on_tunnel_connection_close () {
|
||||
lost ();
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class PortableCoreDeviceBackend : Object, Backend, UsbDeviceBackend {
|
||||
public PairingStore pairing_store {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public bool supports_modeswitch {
|
||||
get {
|
||||
return LibUSB.has_capability (HAS_HOTPLUG) != 0;
|
||||
@@ -811,8 +971,6 @@ namespace Frida.Fruity {
|
||||
private Gee.Map<string, PortableCoreDeviceNetworkTransport> network_transports =
|
||||
new Gee.HashMap<string, PortableCoreDeviceNetworkTransport> ();
|
||||
|
||||
private PairingStore pairing_store = new PairingStore ();
|
||||
|
||||
private MainContext main_context;
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
@@ -832,6 +990,10 @@ namespace Frida.Fruity {
|
||||
|
||||
private delegate void NotifyCompleteFunc ();
|
||||
|
||||
public PortableCoreDeviceBackend (PairingStore pairing_store) {
|
||||
Object (pairing_store: pairing_store);
|
||||
}
|
||||
|
||||
construct {
|
||||
main_context = MainContext.ref_thread_default ();
|
||||
|
||||
@@ -1403,7 +1565,8 @@ namespace Frida.Fruity {
|
||||
supported_by_os = ios_major_version >= 17;
|
||||
}
|
||||
} catch (LockdownError e) {
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
if (!(e is LockdownError.NOT_PAIRED))
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1564,36 +1727,65 @@ namespace Frida.Fruity {
|
||||
public signal void response_received (Bytes response, InetSocketAddress sender);
|
||||
|
||||
public NetworkStack netstack;
|
||||
public UdpSocket sock;
|
||||
public DatagramBasedSource response_source;
|
||||
private Cancellable? io_cancellable;
|
||||
|
||||
private UdpSocket sock;
|
||||
private DatagramBased sock_datagram;
|
||||
|
||||
private Bytes remoted_mdns_request;
|
||||
private InetSocketAddress mdns_address;
|
||||
|
||||
private TimeoutSource retransmit_source;
|
||||
private DatagramBasedSource response_source;
|
||||
|
||||
public ActiveMulticastDnsProbe (InetSocketAddress ifaddr, MainContext main_context, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var local_ip = ifaddr.get_address ();
|
||||
netstack = new SystemNetworkStack (local_ip, ifaddr.scope_id);
|
||||
io_cancellable = cancellable;
|
||||
|
||||
sock = netstack.create_udp_socket ();
|
||||
sock.bind ((InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: local_ip,
|
||||
scope_id: netstack.scope_id
|
||||
));
|
||||
DatagramBased sock_datagram = sock.datagram_based;
|
||||
sock_datagram = sock.datagram_based;
|
||||
|
||||
var remoted_mdns_request = make_remoted_mdns_request ();
|
||||
var mdns_address = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
remoted_mdns_request = make_remoted_mdns_request ();
|
||||
|
||||
mdns_address = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: new InetAddress.from_string ("ff02::fb"),
|
||||
port: 5353,
|
||||
scope_id: netstack.scope_id
|
||||
);
|
||||
Udp.send_to (remoted_mdns_request.get_data (), mdns_address, sock_datagram, cancellable);
|
||||
|
||||
retransmit_source = new TimeoutSource (250);
|
||||
retransmit_source.set_callback (on_retransmit_tick);
|
||||
retransmit_source.attach (main_context);
|
||||
|
||||
response_source = sock_datagram.create_source (IN, cancellable);
|
||||
response_source.set_callback (on_socket_readable);
|
||||
response_source.attach (main_context);
|
||||
|
||||
transmit_request ();
|
||||
}
|
||||
|
||||
public void cancel () {
|
||||
response_source.destroy ();
|
||||
retransmit_source.destroy ();
|
||||
}
|
||||
|
||||
private void transmit_request () throws Error, IOError {
|
||||
Udp.send_to (remoted_mdns_request.get_data (), mdns_address, sock_datagram, io_cancellable);
|
||||
}
|
||||
|
||||
private bool on_retransmit_tick () {
|
||||
try {
|
||||
transmit_request ();
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
|
||||
return Source.CONTINUE;
|
||||
}
|
||||
|
||||
private bool on_socket_readable () {
|
||||
|
||||
@@ -825,7 +825,9 @@ namespace Frida.Fruity.Injector {
|
||||
|
||||
yield restore_main_thread_state (cancellable);
|
||||
|
||||
uint64? libdyld_initialize = dyld_symbols["__ZN5dyld44APIs19_libdyld_initializeEPKNS_16LibSystemHelpersE"];
|
||||
uint64? libdyld_initialize = dyld_symbols["__ZN5dyld44APIs19_libdyld_initializeEv"];
|
||||
if (libdyld_initialize == null)
|
||||
libdyld_initialize = dyld_symbols["__ZN5dyld44APIs19_libdyld_initializeEPKNS_16LibSystemHelpersE"];
|
||||
if (libdyld_initialize != null)
|
||||
yield ensure_libsystem_initialized_for_dyld_v4_and_above (libdyld_initialize, dyld_symbols, cancellable);
|
||||
else
|
||||
@@ -848,9 +850,10 @@ namespace Frida.Fruity.Injector {
|
||||
uint64? notify_objc_init = dyld_symbols["__ZN5dyld412RuntimeState14notifyObjCInitEPKNS_6LoaderE"];
|
||||
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_10KernelArgsEPKN5dyld39MachOFileEPK15DyldSharedCachePv"];
|
||||
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"];
|
||||
if (restart_with_dyld_in_cache != null)
|
||||
|
||||
@@ -584,7 +584,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private bool pcb_is_writable () {
|
||||
return pcb.query_send_buffer_space () > LWIP.Tcp.SEND_LOW_WATERMARK &&
|
||||
return pcb.query_send_buffer_space () > 0 &&
|
||||
pcb.query_send_queue_length () < LWIP.Tcp.SEND_QUEUE_LOW_WATERMARK;
|
||||
}
|
||||
|
||||
@@ -677,7 +677,7 @@ namespace Frida.Fruity {
|
||||
do {
|
||||
flags = COPY;
|
||||
|
||||
size_t len = buffer.length;
|
||||
size_t len = buffer.length - n;
|
||||
if (len > uint16.MAX) {
|
||||
len = uint16.MAX;
|
||||
flags |= MORE;
|
||||
@@ -687,7 +687,7 @@ namespace Frida.Fruity {
|
||||
if (available_space < len) {
|
||||
len = available_space;
|
||||
if (len == 0)
|
||||
return OK;
|
||||
break;
|
||||
}
|
||||
|
||||
write_err = pcb.write (buffer[n:n + len], flags);
|
||||
|
||||
+10
-1
@@ -156,12 +156,21 @@ namespace Frida.Fruity {
|
||||
switch (top) {
|
||||
case 0:
|
||||
switch (bottom) {
|
||||
case 1:
|
||||
return true;
|
||||
case 2:
|
||||
return false;
|
||||
case 3:
|
||||
if ((flags & ValueFlags.ALLOW_TERMINATOR) == 0)
|
||||
throw new Error.PROTOCOL ("Unexpected OPACK terminator");
|
||||
return null;
|
||||
}
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported OPACK type 0x%02x", v);
|
||||
if (bottom < 8)
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported OPACK type 0x%02x", v);
|
||||
return (int64) (v - 8);
|
||||
case 1:
|
||||
case 2:
|
||||
return (int64) (v - 8);
|
||||
case 3:
|
||||
switch (bottom) {
|
||||
case 0: return (int64) reader.read_int8 ();
|
||||
|
||||
+11
-1
@@ -454,7 +454,7 @@ namespace Frida.Fruity {
|
||||
write_entry (entry);
|
||||
|
||||
size_t offset_table_offset = (size_t) seekable.tell ();
|
||||
offset_size = compute_offset_size (sorted_entries.last ().offset);
|
||||
offset_size = compute_offset_size (offset_table_offset);
|
||||
foreach (var entry in sorted_entries)
|
||||
write_offset (entry.offset);
|
||||
|
||||
@@ -1088,6 +1088,11 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public void write_dict (PlistDict dict) {
|
||||
if (dict.is_empty) {
|
||||
write_line ("<dict/>");
|
||||
return;
|
||||
}
|
||||
|
||||
write_line ("<dict>");
|
||||
level++;
|
||||
|
||||
@@ -1114,6 +1119,11 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public void write_array (PlistArray array) {
|
||||
if (array.is_empty) {
|
||||
write_line ("<array/>");
|
||||
return;
|
||||
}
|
||||
|
||||
write_line ("<array>");
|
||||
level++;
|
||||
|
||||
|
||||
+51
-62
@@ -1983,12 +1983,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public sealed class TcpTunnelConnection : Object, TunnelConnection, AsyncInitable {
|
||||
public InetSocketAddress address {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public NetworkStack netstack {
|
||||
public IOStream stream {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
@@ -1999,16 +1994,6 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public TunnelKey local_keypair {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public TunnelKey remote_pubkey {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public InetAddress remote_address {
|
||||
get {
|
||||
return tunnel_params.server_address;
|
||||
@@ -2026,7 +2011,6 @@ namespace Frida.Fruity {
|
||||
private TunnelParameters tunnel_params;
|
||||
private VirtualNetworkStack _tunnel_netstack;
|
||||
|
||||
private TlsClientConnection connection;
|
||||
private BufferedInputStream input;
|
||||
private OutputStream output;
|
||||
|
||||
@@ -2040,7 +2024,38 @@ namespace Frida.Fruity {
|
||||
|
||||
public static async TcpTunnelConnection open (InetSocketAddress address, NetworkStack netstack, TunnelKey local_keypair,
|
||||
TunnelKey remote_pubkey, Cancellable? cancellable = null) throws Error, IOError {
|
||||
var connection = new TcpTunnelConnection (address, netstack, local_keypair, remote_pubkey);
|
||||
var stream = yield netstack.open_tcp_connection (address, cancellable);
|
||||
|
||||
TlsClientConnection tls_connection;
|
||||
try {
|
||||
tls_connection = TlsClientConnection.new (stream, null);
|
||||
} catch (GLib.Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
tls_connection.set_data ("tcp-tunnel-keypair", local_keypair);
|
||||
tls_connection.set_database (null);
|
||||
|
||||
unowned SSL ssl = get_ssl_handle_from_connection (tls_connection);
|
||||
ssl.set_cipher_list ("PSK-AES128-GCM-SHA256:PSK-AES256-GCM-SHA384:PSK-AES256-CBC-SHA384:PSK-AES128-CBC-SHA256");
|
||||
ssl.set_psk_client_callback ((ssl, hint, identity, psk) => {
|
||||
unowned TlsClientConnection conn = (TlsClientConnection) get_connection_from_ssl_handle (ssl);
|
||||
unowned TunnelKey tk = conn.get_data ("tcp-tunnel-keypair");
|
||||
|
||||
Memory.copy (identity, PSK_IDENTITY.data, PSK_IDENTITY.data.length);
|
||||
|
||||
var key = get_raw_private_key (tk.handle).get_data ();
|
||||
Memory.copy (psk, key, key.length);
|
||||
|
||||
return key.length;
|
||||
});
|
||||
|
||||
try {
|
||||
yield tls_connection.handshake_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
|
||||
var connection = new TcpTunnelConnection (tls_connection);
|
||||
|
||||
try {
|
||||
yield connection.init_async (Priority.DEFAULT, cancellable);
|
||||
@@ -2051,46 +2066,29 @@ namespace Frida.Fruity {
|
||||
return connection;
|
||||
}
|
||||
|
||||
private TcpTunnelConnection (InetSocketAddress address, NetworkStack netstack, TunnelKey local_keypair,
|
||||
TunnelKey remote_pubkey) {
|
||||
Object (
|
||||
address: address,
|
||||
netstack: netstack,
|
||||
local_keypair: local_keypair,
|
||||
remote_pubkey: remote_pubkey
|
||||
);
|
||||
public static async TcpTunnelConnection open_stream (IOStream stream, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
var connection = new TcpTunnelConnection (stream);
|
||||
|
||||
try {
|
||||
yield connection.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
private TcpTunnelConnection (IOStream stream) {
|
||||
Object (stream: stream);
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
var stream = yield netstack.open_tcp_connection (address, cancellable);
|
||||
|
||||
try {
|
||||
connection = TlsClientConnection.new (stream, null);
|
||||
} catch (GLib.Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
connection.set_data_full ("tcp-tunnel-connection", this, null);
|
||||
connection.set_database (null);
|
||||
|
||||
unowned SSL ssl = get_ssl_handle_from_connection (connection);
|
||||
ssl.set_cipher_list ("PSK-AES128-GCM-SHA256:PSK-AES256-GCM-SHA384:PSK-AES256-CBC-SHA384:PSK-AES128-CBC-SHA256");
|
||||
ssl.set_psk_client_callback ((ssl, hint, identity, psk) => {
|
||||
unowned TlsClientConnection conn = (TlsClientConnection) get_connection_from_ssl_handle (ssl);
|
||||
TcpTunnelConnection self = conn.get_data ("tcp-tunnel-connection");
|
||||
return self.on_psk_request (ssl, hint, identity, psk);
|
||||
});
|
||||
|
||||
try {
|
||||
yield connection.handshake_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
|
||||
input = (BufferedInputStream) Object.new (typeof (BufferedInputStream),
|
||||
"base-stream", connection.get_input_stream (),
|
||||
"base-stream", stream.get_input_stream (),
|
||||
"close-base-stream", false,
|
||||
"buffer-size", 128 * 1024);
|
||||
output = connection.get_output_stream ();
|
||||
output = stream.get_output_stream ();
|
||||
|
||||
post (make_handshake_request (PREFERRED_MTU));
|
||||
|
||||
@@ -2132,7 +2130,7 @@ namespace Frida.Fruity {
|
||||
yield;
|
||||
|
||||
try {
|
||||
yield connection.close_async ();
|
||||
yield stream.close_async ();
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
|
||||
@@ -2248,15 +2246,6 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
private uint on_psk_request (SSL ssl, string? hint, char[] identity, uint8[] psk) {
|
||||
Memory.copy (identity, PSK_IDENTITY.data, PSK_IDENTITY.data.length);
|
||||
|
||||
var key = get_raw_private_key (local_keypair.handle).get_data ();
|
||||
Memory.copy (psk, key, key.length);
|
||||
|
||||
return key.length;
|
||||
}
|
||||
|
||||
[CCode (cname = "g_tls_connection_openssl_get_ssl")]
|
||||
private extern static unowned SSL get_ssl_handle_from_connection (void * connection);
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import esbuild from 'esbuild';
|
||||
import alias from 'esbuild-plugin-alias';
|
||||
import { createRequire } from 'module';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const BUFFER_SHIM = require.resolve('@frida/buffer');
|
||||
|
||||
esbuild.build({
|
||||
entryPoints: ['index.js'],
|
||||
outfile: 'system-server.js',
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
target: ['es2022'],
|
||||
legalComments: 'none',
|
||||
minify: true,
|
||||
plugins: [
|
||||
alias({
|
||||
'buffer': BUFFER_SHIM,
|
||||
'node:buffer': BUFFER_SHIM,
|
||||
}),
|
||||
],
|
||||
}).catch(() => process.exit(1));
|
||||
@@ -1,3 +1,5 @@
|
||||
import Java from 'frida-java-bridge';
|
||||
|
||||
let ApplicationInfo, Base64OutputStream, Bitmap, ByteArrayOutputStream, Canvas, ComponentName, ContextWrapper, Intent, ResolveInfo,
|
||||
RunningAppProcessInfo, RunningTaskInfo, UserHandle;
|
||||
let ACTION_MAIN, ARGB_8888, CATEGORY_HOME, CATEGORY_LAUNCHER, GET_ACTIVITIES, FLAG_ACTIVITY_NEW_TASK, FLAG_DEBUGGABLE, NO_WRAP, PNG;
|
||||
@@ -0,0 +1,19 @@
|
||||
system_server_js = custom_target('system-server.js',
|
||||
input: [
|
||||
'package.json',
|
||||
'package-lock.json',
|
||||
'build.js',
|
||||
'index.js',
|
||||
],
|
||||
output: [
|
||||
'system-server.js',
|
||||
],
|
||||
command: [
|
||||
python,
|
||||
compile_js_agent,
|
||||
'@OUTPUT@',
|
||||
'@PRIVATE_DIR@',
|
||||
npm,
|
||||
'@INPUT@',
|
||||
],
|
||||
)
|
||||
+563
@@ -0,0 +1,563 @@
|
||||
{
|
||||
"name": "frida-system-server-agent",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "frida-system-server-agent",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@frida/buffer": "^7.0.4",
|
||||
"frida-java-bridge": "^7.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.25.5",
|
||||
"esbuild-plugin-alias": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
|
||||
"integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
|
||||
"integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
|
||||
"integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
|
||||
"integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@frida/buffer": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@frida/buffer/-/buffer-7.0.4.tgz",
|
||||
"integrity": "sha512-RxQ1lZRRiCJj7nhcCiD8xeJx0NsLpGGnjqsmTg7jShGmbnVFMN5W7+J+3gqdPSQhc/IxNBIWc6zRXVp4+qnYHg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.5.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
|
||||
"integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.25.5",
|
||||
"@esbuild/android-arm": "0.25.5",
|
||||
"@esbuild/android-arm64": "0.25.5",
|
||||
"@esbuild/android-x64": "0.25.5",
|
||||
"@esbuild/darwin-arm64": "0.25.5",
|
||||
"@esbuild/darwin-x64": "0.25.5",
|
||||
"@esbuild/freebsd-arm64": "0.25.5",
|
||||
"@esbuild/freebsd-x64": "0.25.5",
|
||||
"@esbuild/linux-arm": "0.25.5",
|
||||
"@esbuild/linux-arm64": "0.25.5",
|
||||
"@esbuild/linux-ia32": "0.25.5",
|
||||
"@esbuild/linux-loong64": "0.25.5",
|
||||
"@esbuild/linux-mips64el": "0.25.5",
|
||||
"@esbuild/linux-ppc64": "0.25.5",
|
||||
"@esbuild/linux-riscv64": "0.25.5",
|
||||
"@esbuild/linux-s390x": "0.25.5",
|
||||
"@esbuild/linux-x64": "0.25.5",
|
||||
"@esbuild/netbsd-arm64": "0.25.5",
|
||||
"@esbuild/netbsd-x64": "0.25.5",
|
||||
"@esbuild/openbsd-arm64": "0.25.5",
|
||||
"@esbuild/openbsd-x64": "0.25.5",
|
||||
"@esbuild/sunos-x64": "0.25.5",
|
||||
"@esbuild/win32-arm64": "0.25.5",
|
||||
"@esbuild/win32-ia32": "0.25.5",
|
||||
"@esbuild/win32-x64": "0.25.5"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-plugin-alias": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-plugin-alias/-/esbuild-plugin-alias-0.2.1.tgz",
|
||||
"integrity": "sha512-jyfL/pwPqaFXyKnj8lP8iLk6Z0m099uXR45aSN8Av1XD4vhvQutxxPzgA2bTcAwQpa1zCXDcWOlhFgyP3GKqhQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/frida-java-bridge": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/frida-java-bridge/-/frida-java-bridge-7.0.4.tgz",
|
||||
"integrity": "sha512-dE5XCvasUwA/wiVb5gSwBPf6dortI8VtD0qLlw2T0ryqX/0lg4BEtEkg1oRr9IlHDVBTjPOqe9KRzdPar8U3EQ==",
|
||||
"license": "LGPL-2.0 WITH WxWindows-exception-3.1"
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "frida-system-server-agent",
|
||||
"version": "1.0.0",
|
||||
"description": "Agent used by Frida's Linux backend on Android",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "node build.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.25.5",
|
||||
"esbuild-plugin-alias": "^0.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@frida/buffer": "^7.0.4",
|
||||
"frida-java-bridge": "^7.0.4"
|
||||
}
|
||||
}
|
||||
@@ -3167,114 +3167,9 @@ namespace Frida {
|
||||
return Linux.syscall (SysCall.tgkill, tgid, tid, sig);
|
||||
}
|
||||
|
||||
private uint linux_major = 0;
|
||||
private uint linux_minor = 0;
|
||||
|
||||
public bool check_kernel_version (uint major, uint minor) {
|
||||
if (linux_major == 0) {
|
||||
var name = Posix.utsname ();
|
||||
name.release.scanf ("%u.%u", out linux_major, out linux_minor);
|
||||
}
|
||||
|
||||
return (linux_major == major && linux_minor >= minor) || linux_major > major;
|
||||
}
|
||||
|
||||
public extern bool _syscall_satisfies (int syscall_id, LinuxSyscall mask);
|
||||
|
||||
public class FileDescriptor : Object, FileDescriptorBased {
|
||||
public int handle;
|
||||
|
||||
public FileDescriptor (int handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
~FileDescriptor () {
|
||||
if (handle != -1)
|
||||
Posix.close (handle);
|
||||
}
|
||||
|
||||
public int steal () {
|
||||
int result = handle;
|
||||
handle = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
public int get_fd () {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PidFileDescriptor : FileDescriptor {
|
||||
private uint pid;
|
||||
|
||||
private PidFileDescriptor (int fd, uint pid) {
|
||||
base (fd);
|
||||
this.pid = pid;
|
||||
}
|
||||
|
||||
public static bool is_supported () {
|
||||
return check_kernel_version (5, 3);
|
||||
}
|
||||
|
||||
public static bool getfd_is_supported () {
|
||||
return check_kernel_version (5, 6);
|
||||
}
|
||||
|
||||
public static PidFileDescriptor from_pid (uint pid) throws Error {
|
||||
int fd = pidfd_open (pid, 0);
|
||||
if (fd == -1)
|
||||
throw_pidfd_error (pid, errno);
|
||||
return new PidFileDescriptor (fd, pid);
|
||||
}
|
||||
|
||||
public FileDescriptor getfd (int targetfd) throws Error {
|
||||
int fd = pidfd_getfd (handle, targetfd, 0);
|
||||
if (fd == -1)
|
||||
throw_pidfd_error (pid, errno);
|
||||
return new FileDescriptor (fd);
|
||||
}
|
||||
|
||||
private static int pidfd_open (uint pid, uint flags) {
|
||||
return Linux.syscall (SysCall.pidfd_open, pid, flags);
|
||||
}
|
||||
|
||||
private static int pidfd_getfd (int pidfd, int targetfd, uint flags) {
|
||||
return Linux.syscall (SysCall.pidfd_getfd, pidfd, targetfd, flags);
|
||||
}
|
||||
|
||||
[NoReturn]
|
||||
private static void throw_pidfd_error (uint pid, int err) throws Error {
|
||||
switch (err) {
|
||||
case Posix.ESRCH:
|
||||
throw new Error.PROCESS_NOT_FOUND ("Process not found");
|
||||
case Posix.EPERM:
|
||||
throw new Error.PERMISSION_DENIED ("Unable to use pidfd for pid %u: %s", pid, strerror (err));
|
||||
default:
|
||||
throw new Error.NOT_SUPPORTED ("Unable to use pidfd for pid %u: %s", pid, strerror (err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace MemoryFileDescriptor {
|
||||
public bool is_supported () {
|
||||
return check_kernel_version (3, 17);
|
||||
}
|
||||
|
||||
public static FileDescriptor from_bytes (string name, Bytes bytes) {
|
||||
assert (is_supported ());
|
||||
|
||||
var fd = new FileDescriptor (memfd_create (name, 0));
|
||||
unowned uint8[] data = bytes.get_data ();
|
||||
ssize_t n = Posix.write (fd.handle, data, data.length);
|
||||
assert (n == data.length);
|
||||
return fd;
|
||||
}
|
||||
|
||||
private int memfd_create (string name, uint flags) {
|
||||
return Linux.syscall (SysCall.memfd_create, name, flags);
|
||||
}
|
||||
}
|
||||
|
||||
private void make_pty (out FileDescriptor read, out FileDescriptor write) throws Error {
|
||||
#if HAVE_OPENPTY
|
||||
int rfd = -1, wfd = -1;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -84,12 +84,6 @@ build_arch () {
|
||||
export CC="gcc -m32" CXX="g++ -m32" STRIP="strip"
|
||||
fi
|
||||
|
||||
case "$ARCH" in
|
||||
armbe8 | arm64be | arm64beilp32)
|
||||
EXTRA_FLAGS+=("--without-prebuilds=sdk:host")
|
||||
;;
|
||||
esac
|
||||
|
||||
cd "$FRIDA_CORE_DIR"
|
||||
|
||||
rm -rf "$BUILD_DIR"
|
||||
|
||||
+11
-6
@@ -1,6 +1,7 @@
|
||||
base_sources = [
|
||||
'frida.vala',
|
||||
'frida-glue.c',
|
||||
'package-manager.vala',
|
||||
'control-service.vala',
|
||||
'portal-service.vala',
|
||||
'host-session-service.vala',
|
||||
@@ -125,6 +126,9 @@ if have_local_backend
|
||||
endif
|
||||
|
||||
if host_os_family == 'darwin'
|
||||
subdir('darwin' / 'agent' / 'reportcrash')
|
||||
subdir('darwin' / 'agent' / 'osanalytics')
|
||||
|
||||
backend_sources += [
|
||||
'darwin' / 'darwin-host-session.vala',
|
||||
'darwin' / 'darwin-host-session-glue.m',
|
||||
@@ -137,8 +141,8 @@ if have_local_backend
|
||||
'darwin' / 'frida-darwin.resources',
|
||||
'darwin' / 'agent' / 'launchd.js',
|
||||
'darwin' / 'agent' / 'xpcproxy.js',
|
||||
'darwin' / 'agent' / 'reportcrash.js',
|
||||
'darwin' / 'agent' / 'osanalytics.js',
|
||||
reportcrash_js,
|
||||
osanalytics_js,
|
||||
],
|
||||
output: [
|
||||
'frida-data-darwin.vapi',
|
||||
@@ -283,6 +287,8 @@ if have_local_backend
|
||||
]
|
||||
|
||||
if host_os == 'android'
|
||||
subdir('linux' / 'agent' / 'system-server')
|
||||
|
||||
backend_vala_args_private += frida_selinux_vala_args
|
||||
backend_libs += frida_selinux
|
||||
backend_deps += frida_selinux_dep
|
||||
@@ -290,7 +296,7 @@ if have_local_backend
|
||||
android_data = custom_target('frida-data-android',
|
||||
input: [
|
||||
'linux' / 'frida-android.resources',
|
||||
'linux' / 'agent' / 'system-server.js',
|
||||
system_server_js,
|
||||
],
|
||||
output: [
|
||||
'frida-data-android.vapi',
|
||||
@@ -359,16 +365,14 @@ if have_local_backend
|
||||
helper_backend_vala_args = [
|
||||
base_vala_args,
|
||||
gum_vala_args,
|
||||
'--pkg=gio-unix-2.0',
|
||||
'--pkg=posix',
|
||||
]
|
||||
helper_backend = static_library('frida-helper-backend', helper_backend_sources,
|
||||
c_args: frida_component_cflags,
|
||||
vala_args: [
|
||||
helper_backend_vala_args,
|
||||
'--vapidir=' + meson.current_source_dir() / 'linux',
|
||||
'--pkg=frida-sysapi',
|
||||
'--pkg=linux',
|
||||
'--pkg=linux-system',
|
||||
'--pkg=frida-gum-linux-1.0',
|
||||
],
|
||||
dependencies: [gio_dep, gio_unix_dep, gee_dep, gum_dep, base_dep] + helper_backend_extra_deps,
|
||||
@@ -629,6 +633,7 @@ core = library('frida-core', core_sources,
|
||||
base_vala_args,
|
||||
gumjs_inspector_vala_args,
|
||||
'--pkg=gmodule-2.0',
|
||||
'--pkg=libsoup-3.0',
|
||||
],
|
||||
vala_gir: core_gir_name,
|
||||
link_args: backend_libs_private,
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/frida-gum.git
|
||||
revision = 16.7.10
|
||||
revision = 17.2.2
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/ngtcp2.git
|
||||
revision = cc5cf523615b7fa7a9340234c7eec3c3645ae969
|
||||
revision = 828dcaed498b40954e1b496664a3309796968db6
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/openssl.git
|
||||
revision = ca4781aaf7910b623d3ae21c6a017e7e9ca6936c
|
||||
revision = 7b86cb6a0c5cb9d79dca012c98a0a30a58eef5b5
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -1,54 +1,27 @@
|
||||
CC := gcc
|
||||
CFLAGS := -Wall -pipe -Os -fPIC -fdata-sections -ffunction-sections
|
||||
LDFLAGS := -Wl,--gc-sections
|
||||
ARCHS := \
|
||||
linux-x86 \
|
||||
linux-x86_64 \
|
||||
linux-arm \
|
||||
linux-armbe8 \
|
||||
linux-armhf \
|
||||
linux-arm64 \
|
||||
linux-arm64be \
|
||||
linux-arm64beilp32 \
|
||||
linux-mips \
|
||||
linux-mipsel \
|
||||
linux-mips64 \
|
||||
linux-mips64el
|
||||
|
||||
all: \
|
||||
sleeper-linux-x86 \
|
||||
sleeper-linux-x86_64 \
|
||||
sleeper-linux-arm \
|
||||
sleeper-linux-armhf \
|
||||
sleeper-linux-arm64 \
|
||||
sleeper-linux-mips \
|
||||
sleeper-linux-mipsel \
|
||||
sleeper-linux-mips64 \
|
||||
sleeper-linux-mips64el \
|
||||
forker-linux-x86 \
|
||||
forker-linux-x86_64 \
|
||||
forker-linux-arm \
|
||||
forker-linux-armhf \
|
||||
forker-linux-arm64 \
|
||||
forker-linux-mips \
|
||||
forker-linux-mipsel \
|
||||
forker-linux-mips64 \
|
||||
forker-linux-mips64el \
|
||||
spawner-linux-x86 \
|
||||
spawner-linux-x86_64 \
|
||||
spawner-linux-arm \
|
||||
spawner-linux-armhf \
|
||||
spawner-linux-arm64 \
|
||||
spawner-linux-mips \
|
||||
spawner-linux-mipsel \
|
||||
spawner-linux-mips64 \
|
||||
spawner-linux-mips64el \
|
||||
simple-agent-linux-x86.so \
|
||||
simple-agent-linux-x86_64.so \
|
||||
simple-agent-linux-arm.so \
|
||||
simple-agent-linux-armhf.so \
|
||||
simple-agent-linux-arm64.so \
|
||||
simple-agent-linux-mips.so \
|
||||
simple-agent-linux-mipsel.so \
|
||||
simple-agent-linux-mips64.so \
|
||||
simple-agent-linux-mips64el.so \
|
||||
resident-agent-linux-x86.so \
|
||||
resident-agent-linux-x86_64.so \
|
||||
resident-agent-linux-arm.so \
|
||||
resident-agent-linux-armhf.so \
|
||||
resident-agent-linux-arm64.so \
|
||||
resident-agent-linux-mips.so \
|
||||
resident-agent-linux-mipsel.so \
|
||||
resident-agent-linux-mips64.so \
|
||||
resident-agent-linux-mips64el.so \
|
||||
$(NULL)
|
||||
all: $(ARCHS) $(NULL)
|
||||
|
||||
define declare-arch
|
||||
$1: resident-agent-$1.so simple-agent-$1.so sleeper-$1 forker-$1 spawner-$1
|
||||
endef
|
||||
|
||||
$(foreach arch,$(ARCHS),$(eval $(call declare-arch,$(arch))))
|
||||
|
||||
define declare-executable
|
||||
$1-linux-x86: $2
|
||||
@@ -66,6 +39,11 @@ $1-linux-arm: $2
|
||||
arm-linux-gnueabi-strip --strip-all $$@.tmp
|
||||
mv $$@.tmp $$@
|
||||
|
||||
$1-linux-armbe8: $2
|
||||
armeb-linux-gnueabi-gcc $$(CFLAGS) $$(LDFLAGS) $$< -o $$@.tmp $3
|
||||
armeb-linux-gnueabi-strip --strip-all $$@.tmp
|
||||
mv $$@.tmp $$@
|
||||
|
||||
$1-linux-armhf: $2
|
||||
arm-linux-gnueabihf-gcc $$(CFLAGS) $$(LDFLAGS) $$< -o $$@.tmp $3
|
||||
arm-linux-gnueabihf-strip --strip-all $$@.tmp
|
||||
@@ -76,6 +54,16 @@ $1-linux-arm64: $2
|
||||
aarch64-linux-gnu-strip --strip-all $$@.tmp
|
||||
mv $$@.tmp $$@
|
||||
|
||||
$1-linux-arm64be: $2
|
||||
aarch64_be-linux-gnu-gcc $$(CFLAGS) $$(LDFLAGS) $$< -o $$@.tmp $3
|
||||
aarch64_be-linux-gnu-strip --strip-all $$@.tmp
|
||||
mv $$@.tmp $$@
|
||||
|
||||
$1-linux-arm64beilp32: $2
|
||||
aarch64_be-linux-gnu_ilp32-gcc $$(CFLAGS) $$(LDFLAGS) $$< -o $$@.tmp $3
|
||||
aarch64_be-linux-gnu_ilp32-strip --strip-all $$@.tmp
|
||||
mv $$@.tmp $$@
|
||||
|
||||
$1-linux-mips: $2
|
||||
mips-linux-gnu-gcc $$(CFLAGS) $$(LDFLAGS) $$< -o $$@.tmp $3
|
||||
mips-linux-gnu-strip --strip-all $$@.tmp
|
||||
@@ -118,6 +106,11 @@ $(eval $(call declare-executable,spawner,spawner-unix.c,-ldl))
|
||||
arm-linux-gnueabi-strip --strip-all $@.tmp
|
||||
mv $@.tmp $@
|
||||
|
||||
%-agent-linux-armbe8.so: %-agent.c
|
||||
armeb-linux-gnueabi-gcc $(CFLAGS) $(LDFLAGS) -shared $< -o $@.tmp
|
||||
armeb-linux-gnueabi-strip --strip-all $@.tmp
|
||||
mv $@.tmp $@
|
||||
|
||||
%-agent-linux-armhf.so: %-agent.c
|
||||
arm-linux-gnueabihf-gcc $(CFLAGS) $(LDFLAGS) -shared $< -o $@.tmp
|
||||
arm-linux-gnueabihf-strip --strip-all $@.tmp
|
||||
@@ -128,6 +121,16 @@ $(eval $(call declare-executable,spawner,spawner-unix.c,-ldl))
|
||||
aarch64-linux-gnu-strip --strip-all $@.tmp
|
||||
mv $@.tmp $@
|
||||
|
||||
%-agent-linux-arm64be.so: %-agent.c
|
||||
aarch64_be-linux-gnu-gcc $(CFLAGS) $(LDFLAGS) -shared $< -o $@.tmp
|
||||
aarch64_be-linux-gnu-strip --strip-all $@.tmp
|
||||
mv $@.tmp $@
|
||||
|
||||
%-agent-linux-arm64beilp32.so: %-agent.c
|
||||
aarch64_be-linux-gnu_ilp32-gcc $(CFLAGS) $(LDFLAGS) -shared $< -o $@.tmp
|
||||
aarch64_be-linux-gnu_ilp32-strip --strip-all $@.tmp
|
||||
mv $@.tmp $@
|
||||
|
||||
%-agent-linux-mips.so: %-agent.c
|
||||
mips-linux-gnu-gcc $(CFLAGS) $(LDFLAGS) -shared $< -o $@.tmp
|
||||
mips-linux-gnu-strip --strip-all $@.tmp
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loaded 100 of 162 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user