如何在Linux下设置录音笔时间
<p>买了一个录音笔,效果比使用笔记本话筒录音好多了还省电。当然啦,我也曾试过使用手机录音,结果是,没能录多久就中断了(Android 就是这么不靠谱)。</p>
<p>
我的录音需要记录较为准确的时间信息。录音笔怎么知道现在是什么时间呢?还好它没有跟风,用不着联网!</p>
<p>
它带了一个小程序,叫「录音笔专用时间同步工具」(英文叫「SetUDiskTime」,可以搜到的)。是一个 EXE 文件,以及一个 DLL 文件。功能很棒,没有广告,没有推荐,也不需要注册什么乱七八糟的账户,甚至都不需要打开浏览器访问人家官网。就弹一个框,显示当前时间,确定一下就设置好时间了。这年头,这么单纯的 Windows 软件还真是难得呢。</p>
<p>
然而,它不支持我用的 Linux 啊。虽然我努力地保证这录音笔一直有电,但是时间还是丢失了几次,它的FAT文件系统也脏了几次。每次我都得开 WinXP 虚拟机来设置时间,好麻烦。</p>
<p>
Wine 是不行的,硬件相关的东西基本上没戏。拿 Procmon 跟踪了一下,也没什么复杂的操作,主要部分就几个 DeviceIoControl 调用,但是看不到调用参数。试了试 IDA,基本看不懂……不过倒是能知道,它通过 IOCTL_SCSI_PASSTHROUGH 直接给设备发送了 SCSI 命令。</p>
<p>
既然跟踪不到,试试抓 USB 的包好了。本来想用 Wireshark 的,但是 WinXP 版的 Wireshark 看来不支持。又尝试了设备分配给 VBox 然后在 Linux 上抓包,结果 permission denied……我是 root 啊都被 deny 了……</p>
<p>
那么,还是在 Windows 上抓包吧。有一个软件叫 USBPcap,下载安装最新版,结果遇到 bug。那试试旧版本吧。官网没给出旧版本的下载地址,不过看到下载链接带上了版本号,这就好办了。去 commit log 里找到旧的版本号替换进去,https://dl.bintray.com/desowin/USBPcap/USBPcapSetup-1.0.0.7.exe,就好了~</p>
<p>
抓好包,取到 Linux 下扔给 Wireshark 解读。挺小的呢,不到50个包,大部分还都是重复的。很快就定位到关键位置了:</p>
<p>
<img title="如何在Linux下设置录音笔时间" alt="如何在Linux下设置录音笔时间" src="https://zhuji.jb51.net/uploads/img/202305/175f18867c5a13ee5d18f0374cd72751.jpg"></p>
<p>
一个 0xcc 命令发过去,设备回复「ACTIONSUSBD」,大概是让设备做好准备。然后一个 0xb0 命令,带上7字节数据发过去,时间就设置好了。简单明了,不像那些小米空气净化器之类的所谓「物联网」,通讯加密起来不让人好好使用。</p>
<p>
那么,这7字节是怎么传递时间数据的呢?我首先检查了UNIX时间戳,对不上。后来发送这个字串看上去挺像YYYYMMDDHHMMSS格式的,只是明显不是当时的时间。啊,它是十六进制的嘛!心算了几个,符合!再拿出我的 Python 牌计算器,确定年份是小端序的16位整数。</p>
<p>
好了,协议细节都弄清楚了,接下来是实现。我原以为我得写个 C 程序,调几个 ioctl 的,后来网友说有个 sg3_utils 包。甚好,直接拿来用 Python 调,省得研究那几个 ioctl 要怎么写。</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterxhtml" id="highlighter_537754">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
<div class="line number10 index9 alt1">
10</div>
<div class="line number11 index10 alt2">
11</div>
<div class="line number12 index11 alt1">
12</div>
<div class="line number13 index12 alt2">
13</div>
<div class="line number14 index13 alt1">
14</div>
<div class="line number15 index14 alt2">
15</div>
<div class="line number16 index15 alt1">
16</div>
<div class="line number17 index16 alt2">
17</div>
<div class="line number18 index17 alt1">
18</div>
<div class="line number19 index18 alt2">
19</div>
<div class="line number20 index19 alt1">
20</div>
<div class="line number21 index20 alt2">
21</div>
<div class="line number22 index21 alt1">
22</div>
<div class="line number23 index22 alt2">
23</div>
<div class="line number24 index23 alt1">
24</div>
<div class="line number25 index24 alt2">
25</div>
<div class="line number26 index25 alt1">
26</div>
<div class="line number27 index26 alt2">
27</div>
<div class="line number28 index27 alt1">
28</div>
<div class="line number29 index28 alt2">
29</div>
<div class="line number30 index29 alt1">
30</div>
<div class="line number31 index30 alt2">
31</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="xhtml plain">#!/usr/bin/env python3</code>
</div>
<div class="line number2 index1 alt1">
<code class="xhtml plain">import os</code>
</div>
<div class="line number3 index2 alt2">
<code class="xhtml plain">import sys</code>
</div>
<div class="line number4 index3 alt1">
<code class="xhtml plain">import struct</code>
</div>
<div class="line number5 index4 alt2">
<code class="xhtml plain">import subprocess</code>
</div>
<div class="line number6 index5 alt1">
<code class="xhtml plain">import datetime</code>
</div>
<div class="line number7 index6 alt2">
<code class="xhtml plain">def set_time(dev):</code>
</div>
<div class="line number8 index7 alt1">
<code class="xhtml spaces"> </code><code class="xhtml plain">cmd = ['sg_raw', '-s', '7', dev, 'b0', '00', '00', '00', '00', '00',</code>
</div>
<div class="line number9 index8 alt2">
<code class="xhtml spaces"> </code><code class="xhtml plain">'00', '07', '00', '00', '00', '00']</code>
</div>
<div class="line number10 index9 alt1">
<code class="xhtml spaces"> </code><code class="xhtml plain">p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stderr=subprocess.PIPE)</code>
</div>
<div class="line number11 index10 alt2">
<code class="xhtml spaces"> </code><code class="xhtml plain">dt = datetime.datetime.now()</code>
</div>
<div class="line number12 index11 alt1">
<code class="xhtml spaces"> </code><code class="xhtml plain">data = struct.pack('<HBBBBB', dt.year, dt.month, dt.day,</code>
</div>
<div class="line number13 index12 alt2">
<code class="xhtml spaces"> </code><code class="xhtml plain">dt.hour, dt.minute, dt.second)</code>
</div>
<div class="line number14 index13 alt1">
<code class="xhtml spaces"> </code><code class="xhtml plain">_, stderr = p.communicate(data)</code>
</div>
<div class="line number15 index14 alt2">
<code class="xhtml spaces"> </code><code class="xhtml plain">ret = p.wait()</code>
</div>
<div class="line number16 index15 alt1">
<code class="xhtml spaces"> </code><code class="xhtml plain">if ret != 0:</code>
</div>
<div class="line number17 index16 alt2">
<code class="xhtml spaces"> </code><code class="xhtml plain">raise subprocess.CalledProcessError(ret, cmd, stderr=stderr)</code>
</div>
<div class="line number18 index17 alt1">
<code class="xhtml plain">def actionsusbd(dev):</code>
</div>
<div class="line number19 index18 alt2">
<code class="xhtml spaces"> </code><code class="xhtml plain">cmd = ['sg_raw', '-r', '11', dev, 'cc', '00', '00', '00', '00', '00',</code>
</div>
<div class="line number20 index19 alt1">
<code class="xhtml spaces"> </code><code class="xhtml plain">'00', '0b', '00', '00', '00', '00']</code>
</div>
<div class="line number21 index20 alt2">
<code class="xhtml spaces"> </code><code class="xhtml plain">subprocess.run(cmd, check=True, stderr=subprocess.PIPE)</code>
</div>
<div class="line number22 index21 alt1">
<code class="xhtml plain">def main():</code>
</div>
<div class="line number23 index22 alt2">
<code class="xhtml spaces"> </code><code class="xhtml plain">if len(sys.argv) != 2:</code>
</div>
<div class="line number24 index23 alt1">
<code class="xhtml spaces"> </code><code class="xhtml plain">sys.exit('usage: setudisktime DEV')</code>
</div>
<div class="line number25 index24 alt2">
<code class="xhtml spaces"> </code><code class="xhtml plain">dev = sys.argv</code>
</div>
<div class="line number26 index25 alt1">
<code class="xhtml spaces"> </code><code class="xhtml plain">if not os.access(dev, os.R_OK | os.W_OK):</code>
</div>
<div class="line number27 index26 alt2">
<code class="xhtml spaces"> </code><code class="xhtml plain">sys.exit(f'insufficient permission for {dev}')</code>
</div>
<div class="line number28 index27 alt1">
<code class="xhtml spaces"> </code><code class="xhtml plain">actionsusbd(dev)</code>
</div>
<div class="line number29 index28 alt2">
<code class="xhtml spaces"> </code><code class="xhtml plain">set_time(dev)</code>
</div>
<div class="line number30 index29 alt1">
<code class="xhtml plain">if __name__ == '__main__':</code>
</div>
<div class="line number31 index30 alt2">
<code class="xhtml spaces"> </code><code class="xhtml plain">main()</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
</p>
頁:
[1]