默认分类

Android USB Gadget 手机模拟真实U盘 操作命令记录

Android USB Gadget 操作命令记录

目标

  • 关闭默认 MTP
  • 保留 ADB
  • 使用 mass_storage 导出一个 30G FAT32 镜像文件
  • backing file 放在 /data/codex_empty_file

连接设备

telnet 10.221.3.83

查看当前 USB 属性

getprop | grep usb
getprop persist.sys.usb.config
getprop sys.usb.config
getprop sys.usb.state

创建空文件并赋权

touch /data/codex_empty_file
chmod 666 /data/codex_empty_file
ls -l /data/codex_empty_file

切换 USB 为 mass_storage + adb

setprop persist.sys.usb.config mass_storage,adb
setprop sys.usb.config mass_storage,adb
sleep 3
getprop persist.sys.usb.config
getprop sys.usb.config
getprop sys.usb.state

创建 30G 镜像并格式化为 FAT32

说明:

  • 先断开当前 gadget 绑定
  • fallocate 创建 30G 镜像文件
  • losetup 挂到 loop 设备
  • mkfs.vfat -F 32 格式化成 FAT32
echo '' > /config/usb_gadget/g1/UDC
echo '' > /config/usb_gadget/g1/functions/mass_storage.0/lun.0/file
rm -f /data/codex_empty_file
fallocate -l 30G /data/codex_empty_file
LOOP=$(losetup -f)
echo $LOOP
losetup $LOOP /data/codex_empty_file
mkfs.vfat -F 32 $LOOP
losetup -d $LOOP

挂回 USB mass_storage

echo /data/codex_empty_file > /config/usb_gadget/g1/functions/mass_storage.0/lun.0/file
echo 0 > /config/usb_gadget/g1/functions/mass_storage.0/lun.0/cdrom
echo 0 > /config/usb_gadget/g1/functions/mass_storage.0/lun.0/ro
echo 1 > /config/usb_gadget/g1/functions/mass_storage.0/lun.0/removable
echo a600000.dwc3 > /config/usb_gadget/g1/UDC

检查最终状态

getprop sys.usb.state
cat /config/usb_gadget/g1/functions/mass_storage.0/lun.0/cdrom
cat /config/usb_gadget/g1/functions/mass_storage.0/lun.0/ro
cat /config/usb_gadget/g1/functions/mass_storage.0/lun.0/removable
cat /config/usb_gadget/g1/functions/mass_storage.0/lun.0/file
ls -lh /data/codex_empty_file

期望结果

sys.usb.state = mass_storage,adb
cdrom         = 0
ro            = 0
removable     = 1
file          = /data/codex_empty_file

常用补充命令

改回仅 ADB

setprop persist.sys.usb.config adb
setprop sys.usb.config adb

纯 HID 键盘临时切换并输入字符串

用途:

  • 临时把手机切成 纯 HID 键盘
  • 向电脑当前焦点窗口输入指定字符串
  • 测试完成后再切回 mass_storage + adb

注意:

  • 纯 HID 模式下,临时不会保留 U 盘功能
  • 切到纯 HID 后,ADB 也可能暂时断开
  • 字符会输入到电脑当前有焦点的输入框

1. 在 g2 上准备纯 HID 键盘 gadget

mkdir -p /config/usb_gadget/g2/configs/b.1/strings/0x409
mkdir -p /config/usb_gadget/g2/strings/0x409
mkdir -p /config/usb_gadget/g2/functions/hid.keyboard
echo 0x18d1 > /config/usb_gadget/g2/idVendor
echo 0x4ee7 > /config/usb_gadget/g2/idProduct
echo 0 > /config/usb_gadget/g2/bDeviceClass
echo 0 > /config/usb_gadget/g2/bDeviceSubClass
echo 0 > /config/usb_gadget/g2/bDeviceProtocol
echo CodexHID > /config/usb_gadget/g2/strings/0x409/product
echo OpenAI > /config/usb_gadget/g2/strings/0x409/manufacturer
echo 1234567890 > /config/usb_gadget/g2/strings/0x409/serialnumber
echo 1 > /config/usb_gadget/g2/functions/hid.keyboard/protocol
echo 1 > /config/usb_gadget/g2/functions/hid.keyboard/subclass
echo 8 > /config/usb_gadget/g2/functions/hid.keyboard/report_length
printf '\x05\x01\x09\x06\xA1\x01\x05\x07\x19\xE0\x29\xE7\x15\x00\x25\x01\x75\x01\x95\x08\x81\x02\x95\x01\x75\x08\x81\x01\x95\x05\x75\x01\x05\x08\x19\x01\x29\x05\x91\x02\x95\x01\x75\x03\x91\x01\x95\x06\x75\x08\x15\x00\x25\x65\x05\x07\x19\x00\x29\x65\x81\x00\xC0' > /config/usb_gadget/g2/functions/hid.keyboard/report_desc
rm -f /config/usb_gadget/g2/configs/b.1/f1
ln -s /config/usb_gadget/g2/functions/hid.keyboard /config/usb_gadget/g2/configs/b.1/f1

2. 切到纯 HID 键盘并输入 qwerrty

setprop sys.usb.config none
sleep 2
echo '' > /config/usb_gadget/g1/UDC 2>/dev/null
echo '' > /config/usb_gadget/g2/UDC 2>/dev/null
sleep 1
echo a600000.dwc3 > /config/usb_gadget/g2/UDC
sleep 4

DEV=/dev/hidg0
[ -e "$DEV" ] || DEV=/dev/hidg1

press() {
  code=$1
  printf "\x00\x00\x$(printf '%02x' $code)\x00\x00\x00\x00\x00" > "$DEV"
  usleep 80000
  printf '\x00\x00\x00\x00\x00\x00\x00\x00' > "$DEV"
  usleep 80000
}

# q w e r r t y
press 20
press 26
press 8
press 21
press 21
press 23
press 28

3. 切回稳定模式 U盘 + ADB

setprop sys.usb.config mass_storage,adb
sh /data/adb/service.d/usb_mass_storage_adb.sh
sleep 4
echo '' > /config/usb_gadget/g2/UDC
sleep 1
echo a600000.dwc3 > /config/usb_gadget/g1/UDC

4. 恢复后检查

getprop sys.usb.config
getprop sys.usb.state
cat /config/usb_gadget/g1/UDC 2>/dev/null
cat /config/usb_gadget/g2/UDC 2>/dev/null
ls -l /config/usb_gadget/g1/configs/b.1

期望结果:

sys.usb.config = mass_storage,adb
sys.usb.state  = mass_storage,adb
g1/UDC         = a600000.dwc3
g2/UDC         = 空

开机自动执行的 Magisk 脚本

脚本路径:

/data/adb/service.d/usb_mass_storage_adb.sh

作用:

  • 开机默认切到 mass_storage,adb
  • 开机后延迟再次执行 setprop persist.sys.usb.config mass_storage,adbsetprop sys.usb.config mass_storage,adb,防止系统后续又切回 mtp,conn_gadget,adb
  • 如果 /data/codex_empty_file 不存在,则自动创建 30G FAT32 镜像
  • 强制设置:

    • cdrom=0
    • ro=0
    • removable=1
  • 自动启动 busybox telnetd -l bash -p 23
  • 当前稳定方案是 U盘 + ADB + telnetd
  • 不再默认强开 MTP,因为三功能同时存在会导致这台设备 USB 不稳定甚至掉线

核心内容如下:

#!/system/bin/sh
PATH=/sbin:/system/sbin:/system/bin:/system/xbin:/data/data/com.termux/files/usr/bin
IMG=/data/codex_empty_file
SIZE=30G
G=/config/usb_gadget/g1
LUN=$G/functions/mass_storage.0/lun.0
UDC=$(getprop sys.usb.controller)
[ -n "$UDC" ] || UDC=$(getprop ro.boot.usbcontroller)

wait_path() {
  p="$1"
  i=0
  while [ ! -e "$p" ] && [ $i -lt 30 ]; do
    sleep 1
    i=$((i + 1))
  done
  [ -e "$p" ]
}

start_telnetd() {
  if command -v ss >/dev/null 2>&1; then
    ss -ltn 2>/dev/null | grep -q ':23 ' && return 0
  fi
  if command -v netstat >/dev/null 2>&1; then
    netstat -ltn 2>/dev/null | grep -q ':23 ' && return 0
  fi
  busybox telnetd -l bash -p 23
}

apply_usb_mode() {
  setprop persist.sys.usb.config mass_storage,adb
  setprop sys.usb.config mass_storage,adb
  sleep 2
  echo '' > "$G/UDC" 2>/dev/null
  echo '' > "$LUN/file" 2>/dev/null
  rm -f "$G/configs/b.1/f1" "$G/configs/b.1/f2" "$G/configs/b.1/f3" "$G/configs/b.1/f4" "$G/configs/b.1/f5" "$G/configs/b.1/f6" 2>/dev/null
  ln -s ../../../../usb_gadget/g1/functions/mass_storage.0 "$G/configs/b.1/f1" 2>/dev/null
  ln -s ../../../../usb_gadget/g1/functions/ffs.adb "$G/configs/b.1/f2" 2>/dev/null
  echo 0 > "$LUN/cdrom" 2>/dev/null
  echo 0 > "$LUN/ro" 2>/dev/null
  echo 1 > "$LUN/removable" 2>/dev/null
  echo "$IMG" > "$LUN/file" 2>/dev/null
  if [ -n "$UDC" ]; then
    echo "$UDC" > "$G/UDC" 2>/dev/null
  fi
}

wait_path "$G/UDC" || exit 1
wait_path "$LUN/file" || exit 1

if [ ! -f "$IMG" ]; then
  rm -f "$IMG"
  fallocate -l "$SIZE" "$IMG" || dd if=/dev/zero of="$IMG" bs=1M count=30720
  LOOP=$(losetup -f 2>/dev/null)
  if [ -n "$LOOP" ]; then
    losetup "$LOOP" "$IMG"
    mkfs.vfat -F 32 "$LOOP"
    losetup -d "$LOOP"
  fi
fi

apply_usb_mode
start_telnetd
(
  sleep 15
  apply_usb_mode
  start_telnetd
  sleep 15
  apply_usb_mode
  start_telnetd
) &

查看当前 gadget 配置

ls -l /config/usb_gadget/g1/configs/b.1
find /config/usb_gadget/g1/functions/mass_storage.0 -maxdepth 3 -type f

重新绑定 USB 控制器

echo '' > /config/usb_gadget/g1/UDC
echo a600000.dwc3 > /config/usb_gadget/g1/UDC

注意事项

  • 如果 Windows 识别成 CD 驱动器,检查 cdrom 是否为 0
  • 如果电脑侧不刷新,重新插拔 USB 或重新绑定 UDC
  • 如果 backing file 被占用,修改 ro 或重新绑定时可能出现 Device or resource busy
  • /sdcard 不适合拿来做这种导出镜像,建议放 /data

回复

This is just a placeholder img.