背景
CTF勉強会のStegano演習でaudioファイルからflagを見つける問題が出た。
audio編集ソフトを入れて解いてください、とのことだったが、人によっては持ってきたPCに自由にフリーソフトを入れられない。
そのためマルチツールであるWebブラウザで解けないか、あるいはOSデフォルトで入っているCLIで解けないか、模索してみた。
なお、答えはタイトルでネタバレしてるので悪しからず。
要件
- playbackRate(N倍速再生)と逆再生ができる
- ブラウザはChrome/Firefoxで動けば良しとする
ブラウザ
✖️HTMLMediaElement
WebAudioAPIより簡単に扱えるが、playbackRateは設定できるものの逆再生のAPIは用意されていない。
そのため自力で実装するしかないが、現状のAPIでは厳しかった。
下記はaudio.duration
で全体の秒数を取得し、そこからpitch
秒刻みで後ろから再生してみたスクリプト。
答えを知ってると少し聞きとれる気もするが、聞き取るのは難しかった。
const audio = new Audio('http://localhost/~darai0512/Stegano.wav');
audio.onloadeddata = () => {
const pitch = 0.1;
let endOffset = audio.duration;
let startOffset = endOffset - pitch;
audio.ontimeupdate = () => {
if (audio.currentTime < endOffset) return;
audio.pause();
startOffset -= pitch;
if (startOffset < 0) return;
endOffset -= pitch;
audio.currentTime = startOffset;
audio.play();
};
audio.currentTime = startOffset;
audio.playbackRate = 0.8;
audio.play();
};
W3C Media Fragments URI
HTMLMediaElement.currentTime
に秒数を入れることでその秒数から再生開始したが、<audio URL>#t=<startOffset>,<endOffset>
でも区間指定の再生が可能
○Web Audio API
audioファイルをArrayBufferで受け、decodeAudioData()でmetadataを分離しArray.prototype.reverse()により逆再生する
demo: https://darai0512.github.io/audio-reverse-play/
- localからaudioファイルを読み込み
fileReader.readAsArrayBuffer()
でArrayBufferに変換する- WebサーバーがたてられるならXHRでArrayBufferを作っても良い
- そのまま
ArrayBuffer.reverse()
するとaudio以外のmetadataも反転されファイル形式がおかしくなるため、AudioContext.decodeAudioData()
にかける -
connect()
=>start()
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<body>
<p>WebAudioAPI playbackRate: <input type="text" id="rate" size="3" value="0.7"/></p>
<input type="file" onchange="handleFile(this.files)">
<button onclick="play(false)">audio play</button>
<button onclick="play(true)">reverse audio play</button>
<script>
const context = new AudioContext();
const fileReader = new FileReader();
let buffer = null;
fileReader.onload = () => {
context.decodeAudioData(fileReader.result, (b) => buffer = b);
}
const handleFile = ([file]) => fileReader.readAsArrayBuffer(file);
let reversed = false;
const play = (reverse) => {
if (buffer === null) return;
if (reverse !== reversed) {
for (let i=0;i<buffer.numberOfChannels;i++) {
buffer.getChannelData(i).reverse();
}
reversed = !reversed;
}
const source = context.createBufferSource();
source.buffer = buffer;
source.playbackRate.value = ((r) => 0 < r ? r : 1)(parseFloat(document.getElementById('rate').value));
source.connect(context.destination);
source.onended = () => source.stop(0);
source.start(0);
};
</script>
CLI
✖️afconvert
- OSX標準のaudio converter
- codec変換に優れている
-
afconvert -hf
で扱えるcodec一覧がみれる afconvert -f <output codec> <input> <output>
- しかしエラー(変換できない)も多い......
-
- reverse変換optionはなさそう
ちなみにaudioファイルのmeta情報を見るのに便利なafinfo
、CLIからrate指定でaudio再生できるafplay
もOSX標準搭載
$afinfo Stegano.wav
File: Stegano.wav
File type ID: WAVE
Num Tracks: 1
----
Data format: 1 ch, 8000 Hz, 'lpcm' (0x0000000C) 16-bit little-endian signed integer
no channel layout.
estimated duration: 3.259625 sec
audio bytes: 52154
audio packets: 26077
bit rate: 128000 bits per second
packet size upper bound: 2
maximum packet size: 2
audio data file offset: 44
not optimized
source bit depth: I16
----
○ffmpeg
- 標準搭載ではないがunix系で使えてインストールしている人も多いであろう鉄板
- OSXなら
brew install ffmpeg
- OSXなら
- ムービーを扱う紹介記事が多いが、audioのみももちろん変換可能
- reverse変換したい場合、videoなら
-vf reverse
、audioなら-af areverse
- reverse変換したい場合、videoなら
$ffmpeg -i Stegano.wav -af areverse reverse.wav
$afplay -r 0.7 reverse.wav
GUIフリーソフト
✖️QuickTime Player
旧バージョンにはmenuバーの中にそうした機能があったが、手元の10.5ではショートカットキーで逆再生・倍速再生を実現できた
内容 | ショートカット |
---|---|
再生 | l |
逆再生 | j |
スロー再生はapplescriptで実現可能(ただし逆再生には利かない。スクリプトエディタはCmd + space
のSpotlight検索で「applescript Editor」で開ける)
tell application "QuickTime Player"
set rate of document 1 to 0.5
end tell
しかし逆再生しても答えが聞き取れなかった......