U 盘刚插进去,资源管理器甚至还没点开,Python 代码已经跑了。第一次看到这种效果,我第一反应不是“挺酷”,而是先看进程。
因为这东西要是出现在一台自己不知情的电脑上,就不是自动化了,是事故。
先把一个容易误解的地方说清楚:普通 U 盘插进 Windows,并不会天然执行里面的 Python 文件。
以前那套 autorun.inf 想怎么玩就怎么玩的日子基本过去了。现在真要实现“插 U 盘触发代码”,靠谱的思路反而很朴素:
电脑上提前跑一个监听程序。
它盯着磁盘变化。
发现新的可移动磁盘后,再判断这个 U 盘是不是自己认可的设备,最后决定要不要启动任务。
我一般不会让它直接执行 U 盘里的 .py 文件。这个口子开得太大。
比如 U 盘里放了这么一个文件:
.trigger
机器上的 Python 程序只认这个标记。发现它之后,执行的是电脑本地已经部署好的脚本。
这样至少不会变成“谁插个 U 盘,都能往机器里塞代码”。
Python 监听可以写得很短。
from pathlib import Path
import subprocess
import sys
import time
import psutil
LOCAL_JOB = Path(r"D:\usb-agent\import_job.py")
seen = set()
defremovable_drives():
drives = {}
for item in psutil.disk_partitions(all=False):
if"removable"notin item.opts.lower():
continue
root = Path(item.mountpoint)
drives[str(root)] = root
return drives
whileTrue:
current = removable_drives()
for key, root in current.items():
if key in seen:
continue
print(f"[usb] detected: {root}")
trigger = root / ".trigger"
ifnot trigger.exists():
print(f"[usb] ignore: marker missing")
continue
print(f"[usb] accepted: {root}")
subprocess.run(
[
sys.executable,
str(LOCAL_JOB),
"--source",
str(root),
],
timeout=60,
check=False,
)
seen = set(current)
time.sleep(1)
这里有个细节我会专门盯一下:
subprocess.run(...)
传进去的是 LOCAL_JOB。
不是:
root / "start.py"
这两个写法看着只差一点,安全边界完全不是一回事。
后者意味着 U 盘里放什么 Python,你就执行什么 Python。
那我肯定不敢这么上。
真正做业务,我还会再加一道设备校验。
比如 U 盘里放一个配置文件:
device_id=ops-import-01
程序读出来以后,只允许白名单里的设备继续。
ALLOWED = {
"ops-import-01",
"ops-import-02",
}
deftrusted_usb(root: Path) -> bool:
identity = root / "device.id"
ifnot identity.is_file():
returnFalse
device_id = identity.read_text(
encoding="utf-8"
).strip()
return device_id in ALLOWED
然后入口改成:
ifnot trusted_usb(root):
print(f"[usb] rejected: {root}")
continue
这时候整个流程就比较像样了:
U盘插入
↓
操作系统挂载磁盘
↓
Python发现新盘符
↓
检查设备标识
↓
检查触发文件
↓
执行机器本地任务
这种东西其实挺适合一些离线环境。
比如测试设备不能联网,需要靠 U 盘导入配置;又或者生产机器需要离线拉取日志,操作人员插盘以后自动把指定目录的数据复制进去。
以前这种流程经常是:
插 U 盘。
打开目录。
找文件。
运行脚本。
选择路径。
拷数据。
最后再弹一句“操作成功”。
步骤一多,人就一定会点错。
换成监听以后,U 盘就是一个触发器。
比如我会把真正的数据处理单独放在 import_job.py 里:
from pathlib import Path
import argparse
import shutil
parser = argparse.ArgumentParser()
parser.add_argument("--source", required=True)
args = parser.parse_args()
usb = Path(args.source)
incoming = usb / "incoming"
target = Path(r"D:\offline-data")
ifnot incoming.is_dir():
raise SystemExit("incoming directory missing")
for src in incoming.glob("*.json"):
if src.stat().st_size > 20 * 1024 * 1024:
print(f"skip oversized file: {src.name}")
continue
dst = target / src.name
shutil.copy2(src, dst)
print(f"imported: {src.name}")
注意我这里连 *.py、*.bat、*.exe 都不碰。
只收明确约定的数据文件。
这类自动化我最怕有人为了省十几行代码,直接写:
subprocess.run(usb / "run.bat")
效果确实炸裂。
U 盘一插,代码就跑。
问题是你已经把“可移动存储设备”变成了“代码投递通道”。
换个人拿一个盘过来,里面的脚本被替换掉,你的监听程序反而成了它最忠实的执行器。
另外还有一种更吓人的设备,看起来长得像 U 盘,插进去以后系统识别的却可能是键盘设备。
这种东西走的就不是“磁盘里放个 Python 文件然后自动运行”这条路了,而是另一套攻击面。碰到陌生 USB 设备,我一般不会因为它长得像存储盘,就默认它真的是存储盘。
所以“插 U 盘执行 Python”这个需求本身没什么玄学。
真正值得盯的是:
到底是谁在触发谁,执行的代码又来自哪里。
如果触发器是 U 盘,代码始终放在本机,并且设备身份、文件类型、执行权限都有限制,这是一套很好用的自动化。
如果变成 U 盘里塞什么,本机就执行什么……
那代码确实跑得挺丝滑。
安全边界也一起跑没了。