CellGuard: LTE/5G-NR lock + IMSI-catcher detection KSU module
- cgcap.ko: cpif CP-frame capture via kprobe/kallsyms, stock-GKI build - ratlock.sh: hold modem to LTE+NR(5G), block 2G/3G downgrade - cgdetect.sh: always-on behavioral cell-site-simulator detection - cgctl.sh + WebUI: block/detect toggles, status + alerts - build-stock-gki.sh: reproducible stock-GKI module build pipeline
This commit is contained in:
27
common/kmod/Makefile
Normal file
27
common/kmod/Makefile
Normal file
@@ -0,0 +1,27 @@
|
||||
# CellGuard kernel module
|
||||
#
|
||||
# Cross-compile against the rango (Pixel 10 Pro Fold, Tensor G5) kernel.
|
||||
# Expects KDIR pointing at a prepared kernel tree (6.6.x).
|
||||
#
|
||||
# export KDIR=/home/snake/RadioControl/build/rango-kernel
|
||||
# export CROSS_COMPILE=aarch64-linux-gnu-
|
||||
# export ARCH=arm64
|
||||
# make
|
||||
#
|
||||
# Output: cellguard.ko
|
||||
#
|
||||
# If KDIR is unset, falls back to the running kernel's build dir
|
||||
# (only useful on a dev machine for sanity-compile).
|
||||
|
||||
obj-m += cellguard.o
|
||||
|
||||
KDIR ?= /lib/modules/$(shell uname -r)/build
|
||||
|
||||
all:
|
||||
$(MAKE) -C $(KDIR) M=$(PWD) modules
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(KDIR) M=$(PWD) clean
|
||||
|
||||
install:
|
||||
@echo "Copy cellguard.ko to device then 'insmod cellguard.ko'"
|
||||
126
common/kmod/build-stock-gki.sh
Executable file
126
common/kmod/build-stock-gki.sh
Executable file
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env bash
|
||||
# build-stock-gki.sh — build cellguard.ko against the STOCK Google GKI kernel
|
||||
# that the target device is running, on an aarch64 build host (e.g. RPi5).
|
||||
#
|
||||
# Why this exists
|
||||
# ---------------
|
||||
# The device is a stock Pixel (rango / Tensor G5) running a stock GKI kernel:
|
||||
# 6.6.102-android15-8-g6eb5b2a8c46b-ab14739656-4k (RANDSTRUCT_NONE, CFI_CLANG,
|
||||
# MODVERSIONS, MODULE_SIG_PROTECT, LTO_NONE)
|
||||
#
|
||||
# For an out-of-tree module to LOAD there:
|
||||
# 1. It must match the kernel ABI: same vermagic *flags* (the release string is
|
||||
# ignored under MODVERSIONS) and matching symbol CRCs. => build against the
|
||||
# exact stock source @ the build's git SHA + the build's vmlinux.symvers.
|
||||
# 2. It must not import GKI *protected* symbols (kernel_read/kernel_write). The
|
||||
# module resolves those at runtime via a kprobe->kallsyms bootstrap instead
|
||||
# (see cellguard.c: cg_resolve_protected). Nothing to do here, just noting it.
|
||||
#
|
||||
# This does NOT require Google's signing key: MODULE_SIG_FORCE is off, so an
|
||||
# unsigned module loads as long as it imports no protected symbols.
|
||||
#
|
||||
# Usage
|
||||
# -----
|
||||
# ./build-stock-gki.sh # auto-detect build info from adb device
|
||||
# BID=14739656 KREL=6.6.102-android15-8-g6eb5b2a8c46b-ab14739656-4k \
|
||||
# KSHA=6eb5b2a8c46b6393ccde67a51f2e2f56277791c6 KBRANCH=android15-6.6-2025-10 \
|
||||
# ./build-stock-gki.sh # or pin everything explicitly
|
||||
#
|
||||
# Requires (Debian/RPiOS): clang lld llvm bc bison flex libssl-dev libelf-dev git curl
|
||||
set -euo pipefail
|
||||
|
||||
# ---- locations -------------------------------------------------------------
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)" # .../CellGuard/common/kmod
|
||||
WORK="${WORK:-$HERE/.gki-build}" # persistent build workspace
|
||||
SRC="$WORK/common" # kernel source tree
|
||||
ART="$WORK/artifacts" # downloaded GKI artifacts
|
||||
mkdir -p "$WORK" "$ART"
|
||||
|
||||
# ---- 1. determine target build --------------------------------------------
|
||||
# KREL = full kernel release; BID = ci.android.com build number; KSHA = kernel
|
||||
# common git sha; KBRANCH = the kernel/common branch to fetch the sha from.
|
||||
if [ -z "${KREL:-}" ] && command -v adb >/dev/null && adb get-state >/dev/null 2>&1; then
|
||||
KREL="$(adb shell 'uname -r' | tr -d '\r')"
|
||||
echo "[*] device kernel release: $KREL"
|
||||
fi
|
||||
: "${KREL:?set KREL (e.g. 6.6.102-android15-8-g<sha12>-ab<bid>-4k) or connect adb}"
|
||||
|
||||
# derive BID (ab#######) and short sha (g############) from the release string
|
||||
BID="${BID:-$(printf '%s' "$KREL" | sed -nE 's/.*-ab([0-9]+)-.*/\1/p')}"
|
||||
GSHORT="$(printf '%s' "$KREL" | sed -nE 's/.*-g([0-9a-f]{12,}).*/\1/p')"
|
||||
: "${BID:?could not derive BID from KREL; pass BID=...}"
|
||||
: "${KBRANCH:=android15-6.6}" # override if the build used a dated branch, e.g. android15-6.6-2025-10
|
||||
|
||||
echo "[*] BID=$BID gsha=$GSHORT branch=$KBRANCH"
|
||||
|
||||
# ---- 2. download stock GKI artifacts for this exact build ------------------
|
||||
# kernel-headers.tar.gz (source headers), vmlinux.symvers (symbol CRCs = our
|
||||
# Module.symvers), abi_symbollist.raw (KMI allow-list), manifest (pins KSHA).
|
||||
RAW="https://ci.android.com/builds/submitted/${BID}/kernel_aarch64/latest/raw"
|
||||
fetch() { # fetch <artifact> -> $ART/<artifact>
|
||||
[ -s "$ART/$1" ] && { echo " cached $1"; return; }
|
||||
echo " download $1"
|
||||
curl -fsSL -o "$ART/$1" "$RAW/$1"
|
||||
}
|
||||
echo "[*] fetching GKI artifacts for ab$BID"
|
||||
fetch "vmlinux.symvers"
|
||||
fetch "abi_symbollist.raw"
|
||||
fetch "manifest_${BID}.xml"
|
||||
fetch "kernel-headers.tar.gz" # not strictly needed (we clone full source) but handy
|
||||
|
||||
# exact kernel/common sha: prefer manifest, fall back to KSHA env
|
||||
if [ -z "${KSHA:-}" ]; then
|
||||
KSHA="$(sed -nE 's/.*path="common"[^>]*revision="([0-9a-f]{40})".*/\1/p' "$ART/manifest_${BID}.xml" | head -1)"
|
||||
fi
|
||||
: "${KSHA:?could not determine kernel sha; pass KSHA=<40hex>}"
|
||||
echo "[*] kernel/common sha: $KSHA"
|
||||
|
||||
# ---- 3. get the exact stock source ----------------------------------------
|
||||
if [ ! -d "$SRC/.git" ]; then
|
||||
echo "[*] cloning kernel/common (shallow) ..."
|
||||
git clone --depth 1 -b "$KBRANCH" \
|
||||
https://android.googlesource.com/kernel/common "$SRC"
|
||||
fi
|
||||
if [ "$(git -C "$SRC" rev-parse HEAD)" != "$KSHA" ]; then
|
||||
echo "[*] fetching exact sha $KSHA ..."
|
||||
git -C "$SRC" fetch --depth 1 origin "$KSHA"
|
||||
git -C "$SRC" checkout -q "$KSHA"
|
||||
fi
|
||||
echo "[*] source at $(git -C "$SRC" rev-parse HEAD)"
|
||||
|
||||
# ---- 4. configure with the DEVICE's real .config --------------------------
|
||||
# Pull /proc/config.gz from the device for a byte-exact config (RANDSTRUCT off,
|
||||
# CFI on, MODVERSIONS on, etc). Fall back to a saved copy if adb is unavailable.
|
||||
if command -v adb >/dev/null && adb get-state >/dev/null 2>&1; then
|
||||
echo "[*] pulling device .config"
|
||||
adb exec-out 'su -c "cat /proc/config.gz"' 2>/dev/null | zcat > "$SRC/.config" \
|
||||
|| adb exec-out 'cat /proc/config.gz' | zcat > "$SRC/.config"
|
||||
elif [ -s "$HERE/device.config" ]; then
|
||||
cp "$HERE/device.config" "$SRC/.config"
|
||||
else
|
||||
echo "!! no .config: connect adb or place device.config next to this script" >&2
|
||||
exit 1
|
||||
fi
|
||||
cp "$ART/vmlinux.symvers" "$SRC/Module.symvers" # real CRCs for MODVERSIONS
|
||||
|
||||
# ---- 5. prepare + build ----------------------------------------------------
|
||||
# Native aarch64 clang (Debian) — kCFI type-IDs are ABI-stable across clang
|
||||
# versions, so this interoperates with the device's clang-18 GKI kernel.
|
||||
export ARCH=arm64 LLVM=1
|
||||
JOBS="${JOBS:-$(nproc)}"
|
||||
echo "[*] olddefconfig + modules_prepare (-j$JOBS) ..."
|
||||
make -C "$SRC" -j"$JOBS" olddefconfig >/dev/null
|
||||
make -C "$SRC" -j"$JOBS" modules_prepare
|
||||
|
||||
echo "[*] building module ..."
|
||||
make -C "$SRC" -j"$JOBS" M="$HERE" modules
|
||||
|
||||
echo
|
||||
echo "[✓] built: $HERE/cellguard.ko"
|
||||
modinfo "$HERE/cellguard.ko" | grep -E 'vermagic|name'
|
||||
echo
|
||||
echo "Load it via the KernelSU module path (service.sh does this on boot):"
|
||||
echo " adb push $HERE/cellguard.ko /data/local/tmp/cellguard.ko"
|
||||
echo " adb shell su -c 'cp /data/local/tmp/cellguard.ko \\"
|
||||
echo " /data/adb/modules/cellguard/common/kmod/cellguard.ko && \\"
|
||||
echo " insmod /data/adb/modules/cellguard/common/kmod/cellguard.ko'"
|
||||
BIN
common/kmod/cellguard-6.6.102-android15-stock.ko
Normal file
BIN
common/kmod/cellguard-6.6.102-android15-stock.ko
Normal file
Binary file not shown.
1080
common/kmod/cellguard.c
Normal file
1080
common/kmod/cellguard.c
Normal file
File diff suppressed because it is too large
Load Diff
BIN
common/kmod/cellguard.ko
Normal file
BIN
common/kmod/cellguard.ko
Normal file
Binary file not shown.
53
common/kmod/cellguard_ioctl.h
Normal file
53
common/kmod/cellguard_ioctl.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* cellguard userspace ioctl header.
|
||||
* Shared with /system/bin/cellguard helper and any app that wants
|
||||
* to drive the kernel-enforced cellular policy directly.
|
||||
*/
|
||||
#ifndef _CELLGUARD_IOCTL_H
|
||||
#define _CELLGUARD_IOCTL_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
#define CG_MAGIC 'C'
|
||||
#define CG_IOC_ENABLE _IO(CG_MAGIC, 1)
|
||||
#define CG_IOC_DISABLE _IO(CG_MAGIC, 2)
|
||||
#define CG_IOC_RELEASE _IO(CG_MAGIC, 3)
|
||||
#define CG_IOC_GET_STATUS _IOR(CG_MAGIC, 4, struct cg_status)
|
||||
#define CG_IOC_SCAN _IOR(CG_MAGIC, 5, struct cg_scan)
|
||||
#define CG_IOC_SET_POLICY _IOW(CG_MAGIC, 6, struct cg_policy)
|
||||
|
||||
#define CG_SCAN_BUF_SIZE 16000
|
||||
|
||||
struct cg_status {
|
||||
__s32 enabled;
|
||||
__s32 modem_ok;
|
||||
char modem_path[64];
|
||||
char rat[16];
|
||||
__s32 rat_code;
|
||||
__s32 cipher_known;
|
||||
__s32 cipher_null;
|
||||
char operator_name[32];
|
||||
char mcc_mnc[16];
|
||||
__s32 rsrp;
|
||||
__s32 tx_pwr;
|
||||
__u64 downgrades_blocked;
|
||||
__u64 relocks;
|
||||
__u64 polls;
|
||||
};
|
||||
|
||||
struct cg_scan {
|
||||
char data[CG_SCAN_BUF_SIZE];
|
||||
__u32 data_len;
|
||||
__s32 status;
|
||||
};
|
||||
|
||||
struct cg_policy {
|
||||
__s32 block_2g;
|
||||
__s32 block_3g;
|
||||
__s32 block_null_cipher;
|
||||
__s32 poll_interval_ms;
|
||||
};
|
||||
|
||||
#endif /* _CELLGUARD_IOCTL_H */
|
||||
304
common/kmod/cgcap.c
Normal file
304
common/kmod/cgcap.c
Normal file
@@ -0,0 +1,304 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* cgcap.ko — Shannon SIT/RCM frame capture (read-only)
|
||||
*
|
||||
* Kprobes cpif's ipc_write() — the .write of the FMT/RAW char devices
|
||||
* (/dev/umts_ipc0 ch 0xf5, /dev/oem_ipc0 ch 0x81, ...). rild/libsitril
|
||||
* write the BARE SIT frame there and cpif prepends the SIPC5 link header
|
||||
* in-kernel, so the buffer handed to ipc_write IS the exact SIT payload.
|
||||
*
|
||||
* We copy that payload out (nofault, atomic-safe), tag it with the io_device
|
||||
* channel id, and expose a decoded ring at /proc/cgcap/frames. This lets us
|
||||
* learn the real on-wire command bytes for setAllowedNetworkType / band-lock /
|
||||
* null-cipher by triggering those operations from Android and reading what
|
||||
* rild actually sent — no transmission, no guessing.
|
||||
*
|
||||
* SIT/RCM header (LE), proven from vendor.radio.protocol.sit.base.so:
|
||||
* off0 u8 type 0=REQ 1=RESP 2=IND
|
||||
* off2 u16 id (hi=group/main, lo=sub-cmd)
|
||||
* off4 u16 len total frame length incl header
|
||||
* off6 u32 token transaction id (REQ/RESP only)
|
||||
* off10 u8 error (RESP only)
|
||||
* off12 params
|
||||
* io_device->ch lives at struct offset 0x114 (proven from cpif_probe).
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("snake");
|
||||
MODULE_DESCRIPTION("Shannon SIT/RCM frame capture via cpif ipc_write kprobe");
|
||||
MODULE_VERSION("1.0");
|
||||
|
||||
#define IOD_CH_OFFSET 0x114 /* io_device->ch (u8/u16), cpif_probe RE */
|
||||
#define CAP_DATA 160 /* bytes of payload kept per frame */
|
||||
#define CAP_RING 512 /* ring capacity */
|
||||
|
||||
struct cap_ent {
|
||||
u64 seq;
|
||||
u64 ns;
|
||||
u32 ch;
|
||||
u32 len; /* full byte count of the transfer */
|
||||
u32 n; /* bytes actually captured */
|
||||
u8 dir; /* 0 = TX (ipc_write), 1 = RX (ipc_read) */
|
||||
u8 data[CAP_DATA];
|
||||
};
|
||||
|
||||
static struct cap_ent ring[CAP_RING];
|
||||
static u32 ring_head;
|
||||
static u64 ring_seq;
|
||||
static DEFINE_SPINLOCK(ring_lock);
|
||||
|
||||
/* Optional channel filter: 0 = capture all; else only this iod->ch. */
|
||||
static int cap_ch = 0;
|
||||
module_param(cap_ch, int, 0644);
|
||||
MODULE_PARM_DESC(cap_ch, "capture only this cpif channel id (0=all)");
|
||||
|
||||
/* nofault readers, resolved via kallsyms (may be GKI-protected). */
|
||||
static long (*p_cfu_nofault)(void *dst, const void __user *src, size_t size);
|
||||
static long (*p_cfk_nofault)(void *dst, const void *src, size_t size);
|
||||
|
||||
static unsigned long cg_lookup_name(const char *name)
|
||||
{
|
||||
static unsigned long (*kln)(const char *name);
|
||||
|
||||
if (!kln) {
|
||||
struct kprobe kp = { .symbol_name = "kallsyms_lookup_name" };
|
||||
int ret = register_kprobe(&kp);
|
||||
|
||||
if (ret < 0) {
|
||||
pr_err("cgcap: kprobe bootstrap failed (%d)\n", ret);
|
||||
return 0;
|
||||
}
|
||||
kln = (void *)kp.addr;
|
||||
unregister_kprobe(&kp);
|
||||
}
|
||||
return kln ? kln(name) : 0;
|
||||
}
|
||||
|
||||
/* iod = file->private_data; iod->ch @0x114 (proven from cpif_probe). */
|
||||
static u32 iod_ch(struct file *f)
|
||||
{
|
||||
u16 v;
|
||||
|
||||
if (f && f->private_data &&
|
||||
!get_kernel_nofault(v, (u16 *)((char *)f->private_data + IOD_CH_OFFSET)))
|
||||
return v;
|
||||
return 0xffffffff;
|
||||
}
|
||||
|
||||
static void store_frame(u8 dir, u32 ch, size_t count, const void __user *ubuf)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct cap_ent *e;
|
||||
u32 n = min_t(u32, (u32)count, CAP_DATA);
|
||||
|
||||
if (cap_ch && ch != (u32)cap_ch)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&ring_lock, flags);
|
||||
e = &ring[ring_head];
|
||||
e->seq = ++ring_seq;
|
||||
e->ns = ktime_get_ns();
|
||||
e->ch = ch;
|
||||
e->len = (u32)count;
|
||||
e->dir = dir;
|
||||
e->n = n;
|
||||
if (!p_cfu_nofault || p_cfu_nofault(e->data, ubuf, n))
|
||||
e->n = 0;
|
||||
ring_head = (ring_head + 1) % CAP_RING;
|
||||
spin_unlock_irqrestore(&ring_lock, flags);
|
||||
}
|
||||
|
||||
/* TX: ipc_write(file, user_buf, count, pos): arm64 x0=f, x1=buf, x2=count. */
|
||||
static int cap_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct file *f = (struct file *)regs->regs[0];
|
||||
const void __user *ubuf = (const void __user *)regs->regs[1];
|
||||
size_t count = (size_t)regs->regs[2];
|
||||
|
||||
if (f && ubuf && count)
|
||||
store_frame(0, iod_ch(f), count, ubuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct kprobe kp_ipc_write = {
|
||||
.symbol_name = "ipc_write",
|
||||
.pre_handler = cap_pre,
|
||||
};
|
||||
|
||||
/* Store from a KERNEL buffer (skb->data), read as system — no user copy. */
|
||||
static void store_kframe(u8 dir, u32 ch, const void *kbuf, u32 len)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct cap_ent *e;
|
||||
u32 n = min_t(u32, len, CAP_DATA);
|
||||
|
||||
if (cap_ch && ch != (u32)cap_ch)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&ring_lock, flags);
|
||||
e = &ring[ring_head];
|
||||
e->seq = ++ring_seq;
|
||||
e->ns = ktime_get_ns();
|
||||
e->ch = ch;
|
||||
e->len = len;
|
||||
e->dir = dir;
|
||||
e->n = n;
|
||||
if (!p_cfk_nofault || p_cfk_nofault(e->data, kbuf, n))
|
||||
e->n = 0;
|
||||
ring_head = (ring_head + 1) % CAP_RING;
|
||||
spin_unlock_irqrestore(&ring_lock, flags);
|
||||
}
|
||||
|
||||
/* RX: io_dev_recv_skb_single_from_link_dev(x0 = mld, x1 = iod, x2 = skb) —
|
||||
* the common CP->AP entry for BOTH FMT and RAW channels, skb in kernel memory.
|
||||
* Read skb->data directly (as root/system), no copy_from_user. */
|
||||
static int rx_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
void *iod = (void *)regs->regs[1];
|
||||
struct sk_buff *skb = (struct sk_buff *)regs->regs[2];
|
||||
u16 v;
|
||||
u32 ch = 0xffffffff;
|
||||
|
||||
if (!skb)
|
||||
return 0;
|
||||
if (iod && !get_kernel_nofault(v, (u16 *)((char *)iod + IOD_CH_OFFSET)))
|
||||
ch = v;
|
||||
store_kframe(1, ch, skb->data, skb->len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct kprobe kp_rx = {
|
||||
.symbol_name = "io_dev_recv_skb_single_from_link_dev",
|
||||
.pre_handler = rx_pre,
|
||||
};
|
||||
|
||||
/* ---- /proc/cgcap/frames ---- */
|
||||
|
||||
static const char *sit_type(u8 t)
|
||||
{
|
||||
switch (t) {
|
||||
case 0: return "REQ";
|
||||
case 1: return "RSP";
|
||||
case 2: return "IND";
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
|
||||
static int frames_show(struct seq_file *m, void *v)
|
||||
{
|
||||
static struct cap_ent snap[CAP_RING];
|
||||
unsigned long flags;
|
||||
u32 i, cnt;
|
||||
|
||||
spin_lock_irqsave(&ring_lock, flags);
|
||||
memcpy(snap, ring, sizeof(snap));
|
||||
cnt = ring_head; /* not strictly needed; we sort by seq */
|
||||
(void)cnt;
|
||||
spin_unlock_irqrestore(&ring_lock, flags);
|
||||
|
||||
seq_printf(m, "# total=%llu fields: seq dir ch type id(grp/cmd) len token err payload\n",
|
||||
ring_seq);
|
||||
for (i = 0; i < CAP_RING; i++) {
|
||||
struct cap_ent *e = &snap[i];
|
||||
u8 type, err = 0;
|
||||
u16 id = 0, flen = 0;
|
||||
u32 tok = 0, j;
|
||||
|
||||
if (!e->seq)
|
||||
continue;
|
||||
if (e->n == 0) {
|
||||
seq_printf(m, "%llu %s ch=0x%02x len=%u [nocopy]\n",
|
||||
e->seq, e->dir ? "RX" : "TX", e->ch, e->len);
|
||||
continue;
|
||||
}
|
||||
|
||||
type = e->data[0];
|
||||
if (e->n >= 6) {
|
||||
id = e->data[2] | (e->data[3] << 8);
|
||||
flen = e->data[4] | (e->data[5] << 8);
|
||||
}
|
||||
if (e->n >= 10)
|
||||
tok = e->data[6] | (e->data[7] << 8) |
|
||||
(e->data[8] << 16) | (e->data[9] << 24);
|
||||
if (type == 1 && e->n >= 11)
|
||||
err = e->data[10];
|
||||
|
||||
seq_printf(m, "%llu %s ch=0x%02x %s id=0x%04x(%02x/%02x) len=%u tok=%u err=%u ",
|
||||
e->seq, e->dir ? "RX" : "TX", e->ch, sit_type(type), id,
|
||||
(id >> 8) & 0xff, id & 0xff, flen, tok, err);
|
||||
for (j = 0; j < e->n; j++)
|
||||
seq_printf(m, "%02x", e->data[j]);
|
||||
if (e->len > e->n)
|
||||
seq_printf(m, "..(+%u)", e->len - e->n);
|
||||
seq_putc(m, '\n');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int frames_open(struct inode *i, struct file *f)
|
||||
{ return single_open(f, frames_show, NULL); }
|
||||
|
||||
static const struct proc_ops frames_pops = {
|
||||
.proc_open = frames_open,
|
||||
.proc_read = seq_read,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = single_release,
|
||||
};
|
||||
|
||||
static struct proc_dir_entry *proc_root;
|
||||
|
||||
static int __init cgcap_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
p_cfu_nofault = (void *)cg_lookup_name("copy_from_user_nofault");
|
||||
p_cfk_nofault = (void *)cg_lookup_name("copy_from_kernel_nofault");
|
||||
if (!p_cfu_nofault || !p_cfk_nofault)
|
||||
pr_warn("cgcap: nofault reader unresolved (u=%px k=%px); some payloads empty\n",
|
||||
p_cfu_nofault, p_cfk_nofault);
|
||||
|
||||
ret = register_kprobe(&kp_ipc_write);
|
||||
if (ret < 0) {
|
||||
pr_err("cgcap: register_kprobe(ipc_write) failed (%d) — is cpif loaded?\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = register_kprobe(&kp_rx);
|
||||
if (ret < 0)
|
||||
pr_warn("cgcap: register_kprobe(queue_skb_to_iod) failed (%d); TX-only\n", ret);
|
||||
|
||||
proc_root = proc_mkdir("cgcap", NULL);
|
||||
if (proc_root)
|
||||
proc_create("frames", 0444, proc_root, &frames_pops);
|
||||
|
||||
pr_info("cgcap: capturing cpif ipc_write @ %p (filter ch=0x%x); read /proc/cgcap/frames\n",
|
||||
kp_ipc_write.addr, cap_ch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cgcap_exit(void)
|
||||
{
|
||||
unregister_kprobe(&kp_ipc_write);
|
||||
unregister_kprobe(&kp_rx);
|
||||
if (proc_root) {
|
||||
remove_proc_entry("frames", proc_root);
|
||||
proc_remove(proc_root);
|
||||
}
|
||||
pr_info("cgcap: unloaded (%llu frames seen)\n", ring_seq);
|
||||
}
|
||||
|
||||
module_init(cgcap_init);
|
||||
module_exit(cgcap_exit);
|
||||
BIN
common/kmod/cgcap.ko
Normal file
BIN
common/kmod/cgcap.ko
Normal file
Binary file not shown.
Reference in New Issue
Block a user