您好,登錄后才能下訂單哦!
買了一個錄音筆,效果比使用筆記本話筒錄音好多了還省電。當然啦,我也曾試過使用手機錄音,結果是,沒能錄多久就中斷了(Android 就是這么不靠譜)。
我的錄音需要記錄較為準確的時間信息。錄音筆怎么知道現在是什么時間呢?還好它沒有跟風,用不著聯網!
它帶了一個小程序,叫「錄音筆專用時間同步工具」(英文叫「SetUDiskTime」,可以搜到的)。是一個 EXE 文件,以及一個 DLL 文件。功能很棒,沒有廣告,沒有推薦,也不需要注冊什么亂七八糟的賬戶,甚至都不需要打開瀏覽器訪問人家官網。就彈一個框,顯示當前時間,確定一下就設置好時間了。這年頭,這么單純的 Windows 軟件還真是難得呢。
然而,它不支持我用的 Linux 啊。雖然我努力地保證這錄音筆一直有電,但是時間還是丟失了幾次,它的FAT文件系統也臟了幾次。每次我都得開 WinXP 虛擬機來設置時間,好麻煩。
Wine 是不行的,硬件相關的東西基本上沒戲。拿 Procmon 跟蹤了一下,也沒什么復雜的操作,主要部分就幾個 DeviceIoControl 調用,但是看不到調用參數。試了試 IDA,基本看不懂……不過倒是能知道,它通過 IOCTL_SCSI_PASSTHROUGH 直接給設備發送了 SCSI 命令。
既然跟蹤不到,試試抓 USB 的包好了。本來想用 Wireshark 的,但是 WinXP 版的 Wireshark 看來不支持。又嘗試了設備分配給 VBox 然后在 Linux 上抓包,結果 permission denied……我是 root 啊都被 deny 了……
那么,還是在 Windows 上抓包吧。有一個軟件叫 USBPcap,下載安裝最新版,結果遇到 bug。那試試舊版本吧。官網沒給出舊版本的下載地址,不過看到下載鏈接帶上了版本號,這就好辦了。去 commit log 里找到舊的版本號替換進去,https://dl.bintray.com/desowin/USBPcap/USBPcapSetup-1.0.0.7.exe,就好了~
抓好包,取到 Linux 下扔給 Wireshark 解讀。挺小的呢,不到50個包,大部分還都是重復的。很快就定位到關鍵位置了:
一個 0xcc 命令發過去,設備回復「ACTIONSUSBD」,大概是讓設備做好準備。然后一個 0xb0 命令,帶上7字節數據發過去,時間就設置好了。簡單明了,不像那些小米空氣凈化器之類的所謂「物聯網」,通訊加密起來不讓人好好使用。
那么,這7字節是怎么傳遞時間數據的呢?我首先檢查了UNIX時間戳,對不上。后來發送這個字串看上去挺像YYYYMMDDHHMMSS格式的,只是明顯不是當時的時間。啊,它是十六進制的嘛!心算了幾個,符合!再拿出我的 Python 牌計算器,確定年份是小端序的16位整數。
好了,協議細節都弄清楚了,接下來是實現。我原以為我得寫個 C 程序,調幾個 ioctl 的,后來網友說有個 sg3_utils 包。甚好,直接拿來用 Python 調,省得研究那幾個 ioctl 要怎么寫。
#!/usr/bin/env python3 import os import sys import struct import subprocess import datetime def set_time(dev): cmd = ['sg_raw', '-s', '7', dev, 'b0', '00', '00', '00', '00', '00', '00', '07', '00', '00', '00', '00'] p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stderr=subprocess.PIPE) dt = datetime.datetime.now() data = struct.pack('<HBBBBB', dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second) _, stderr = p.communicate(data) ret = p.wait() if ret != 0: raise subprocess.CalledProcessError(ret, cmd, stderr=stderr) def actionsusbd(dev): cmd = ['sg_raw', '-r', '11', dev, 'cc', '00', '00', '00', '00', '00', '00', '0b', '00', '00', '00', '00'] subprocess.run(cmd, check=True, stderr=subprocess.PIPE) def main(): if len(sys.argv) != 2: sys.exit('usage: setudisktime DEV') dev = sys.argv[1] if not os.access(dev, os.R_OK | os.W_OK): sys.exit(f'insufficient permission for {dev}') actionsusbd(dev) set_time(dev) if __name__ == '__main__': main()
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。