Skip to content

Latest commit

 

History

History
68 lines (49 loc) · 4.4 KB

File metadata and controls

68 lines (49 loc) · 4.4 KB

DuckyIDE Developer Guide & Technical Architecture

This document outlines the critical technical decisions, architectural patterns, and "lessons learned" during the development of DuckyIDE. It serves as a reference for future contributors to understand why the code is written the way it is.

1. USB Gadget Orchestration ("The Total Control Strategy")

Android's default init system aggressively manages the sys.usb.config property. Simply appending "hid" to this property often fails because the OS doesn't know how to construct a custom composite gadget.

The Problem

  • Conflict: Setting sys.usb.config triggers Android's USB HAL, which wipes manual changes to ConfigFS.
  • Ordering: Windows is picky about Interface Descriptor ordering. If ADB isn't first, or if interfaces aren't numbered sequentially, the driver installation fails.
  • Naming: Strict kernels (Samsung/Pixel) require function symlinks in configs/b.1/ to be named sequentially (e.g., f1, f2, f3) rather than their source names (e.g., mass_storage.0).

The Solution: UsbController.java

We implemented a "Total Control" workflow that mimics Kali Nethunter's boot scripts:

  1. Disable UDC: echo "" > UDC stops the gadget physically.
  2. Kill the Daemon: stop adbd ensures the debugger doesn't fight us.
  3. Reset State: setprop sys.usb.config none forces the OS to release its grip on ConfigFS.
  4. Manual Composition: We manually symlink functions from /functions/ to /configs/b.1/.
    • Ordering: ADB (f1) -> RNDIS (f2) -> MTP (f3) -> Mass Storage (f4) -> HID (f5).
    • Dynamic Naming: We assume the kernel needs aliases. We check for ffs.adb but link it as f1.
  5. Re-Enable: Write the UDC name back to UDC and manually restart adbd.

2. HID Injection Strategy

Sending keystrokes to /dev/hidg0 sounds simple, but doing it reliably and fast on Android is difficult.

Evolution of Methods

  1. echo -ne "\x...":
    • Failure: Android shells (mksh/toybox) behave inconsistently with escape sequences. \x00 often gets eaten or misinterpreted, leading to "stuck keys" (modifiers not releasing).
  2. Binary Tool (hid-keyboard):
    • Failure: Spawning a new process for every single keystroke (Runtime.exec) is incredibly slow (10-20ms overhead per key). A long script takes minutes to type.
  3. Current Solution: Base64 Stream Pipelining (Implemented in DuckyParser.java)

The "Base64 + dd" Pipeline

To achieve atomic, binary-safe, and fast injection:

  1. Java Buffering: The DuckyParser converts the entire Ducky Script into a raw byte[] array in memory (Java). Each keystroke is an 8-byte packet (Mod, Res, Key, 0, 0, 0, 0, 0).
  2. Encoding: We Base64 encode this binary blob.
  3. Atomic Delivery: We generate a single shell command:
    echo "BASE64_STRING..." | base64 -d | dd of=/dev/hidg0 bs=8 2>/dev/null
    • base64 -d: Decodes the data back to raw binary on the device side.
    • dd of=/dev/hidg0 bs=8: Writes to the driver in 8-byte blocks. This is crucial. If you write 7 bytes or 9 bytes, the HID driver rejects the packet. dd guarantees block alignment.

3. Root Shell & Error Handling

Standard Runtime.exec() is insufficient because it doesn't capture exit codes or stderr effectively for chained commands.

  • RootShell.java: Uses a custom CommandResult class.
  • It executes commands via su and appends a marker (echo ::::EXITCODE::::$?::::) to stdout.
  • This allows us to parse the exact exit code of the root command and throw Java IOExceptions if a specific step in the USB setup fails.

4. File System & Assets

  • Logging: Logs are written to Context.getExternalFilesDir() (/sdcard/Android/data/...) so they persist even if the app crashes, viewable via PC.
  • Dependencies: We previously used hid-keyboard binaries but moved to the Base64 method to remove binary dependencies and architecture mismatches (ARM vs ARM64).

5. Key Mappings

  • DuckyParser: Contains a hardcoded ASCII_MAP mapping characters to HID usage codes (e.g., 'a' -> 0x04).
  • Modifiers: Supports explicit modifiers (CTRL, ALT, GUI, SHIFT) via bitwise OR operations on the modifier byte (Byte 0 of the report).
  • Protocol: We strictly use the Boot Keyboard Protocol (8 bytes). Some kernels support 7 bytes, but 8 is the standard.

Generated by Gemini CLI Agent