利用本文介绍的知识可以实现优酷网等播放视频时声音的抓取,及所有以wave系列API为渲染接口的软件声音的抓取。
Windows下的声音播放函数有两类,一类是旧的winmm.dll 里面的API,另一类是比较新的DirectSound。
录音的策略可以分为三类:
第一类是利用windows的Mixer系列API(适用于DirectSound),稍微差一点的录音软件会用这种,(这种方法的好处是可以抓取wave系列及Direct系列的声音,不足之处是只能通过混音抓取,这样的缺点就是万一有麦克风开起来,效果可能比较差)
第二类是利用钩子函数,当系统调用某个函数进行播放时,我们对其数据进行抓取,获得其中的数据后,将数据返还给系统调用(只适用与winmm.dll里的函数,DirectSound的API我没有找到直接对数据操作的,DirectSound是用了两个缓冲,然后系统会自动混音,没有找到类似于wave系列的数据接口),今天准备介绍这种方法。
第三类是编写虚拟声卡(专业级都是利用这种),所有流到物理声卡的数据,都会先进入虚拟声卡,Total Recorder等比较著名的录音软件都是采取这个策略。这个还不会,驱动程序没写过。
对于旧的多媒体接口,通过dumpbin.exe -exports winmm.dll ,我们可以看到这个dll导出了很多函数,
包括混音类API(第一类录音策略用下面的API,国内有一些商业化的软件就是这么搞得),如下:
115 70 000094F8 mixerClose
116 71 000107E1 mixerGetControlDetailsA
117 72 0000A479 mixerGetControlDetailsW
118 73 00010456 mixerGetDevCapsA
119 74 0000A183 mixerGetDevCapsW
120 75 0000928F mixerGetID
121 76 000106B2 mixerGetLineControlsA
122 77 0000A05B mixerGetLineControlsW
123 78 000105F3 mixerGetLineInfoA
124 79 0000A25F mixerGetLineInfoW
125 7A 00010116 mixerGetNumDevs
126 7B 0000A02A mixerMessage
127 7C 000095E1 mixerOpen
128 7D 000102D4 mixerSetControlDetails
以及传统的Wave系列API等,今天我们准备用钩子函数来抓取这些API,其中参考了VC知识库的Skype答录机这篇文章,其实主要也是人家的思路,如下:
169 A6 0001C291 waveInAddBuffer
170 A7 0001C0DD waveInClose
171 A8 0001BE87 waveInGetDevCapsA
172 A9 00009C40 waveInGetDevCapsW
173 AA 0001BB4A waveInGetErrorTextA
174 AB 0001C0A1 waveInGetErrorTextW
175 AC 0001C4AC waveInGetID
176 AD 00005FED waveInGetNumDevs
177 AE 0001C3C5 waveInGetPosition
178 AF 0000896A waveInMessage
179 B0 00008EA9 waveInOpen
180 B1 0001C18C waveInPrepareHeader
181 B2 0001C376 waveInReset
182 B3 0001C2FA waveInStart
183 B4 0001C338 waveInStop
184 B5 0001C203 waveInUnprepareHeader
185 B6 0001BC42 waveOutBreakLoop
186 B7 00005726 waveOutClose
187 B8 0001B784 waveOutGetDevCapsA
188 B9 00007E94 waveOutGetDevCapsW
189 BA 0001BB4A waveOutGetErrorTextA
190 BB 0001C0A1 waveOutGetErrorTextW
191 BC 0001C4F9 waveOutGetID
192 BD 00005FB6 waveOutGetNumDevs
193 BE 0001BCD3 waveOutGetPitch
194 BF 0001BD63 waveOutGetPlaybackRate
195 C0 0001BC80 waveOutGetPosition
196 C1 0001B9B2 waveOutGetVolume
197 C2 00006110 waveOutMessage
198 C3 00005201 waveOutOpen
199 C4 0001BB77 waveOutPause
200 C5 000059D9 waveOutPrepareHeader
201 C6 0001BBF3 waveOutReset
202 C7 0001BBB5 waveOutRestart
203 C8 0001BD24 waveOutSetPitch
204 C9 0001BDB4 waveOutSetPlaybackRate
205 CA 0001BA3F waveOutSetVolume
206 CB 000057C8 waveOutUnprepareHeader
207 CC 00005A4A waveOutWrite
虚拟声卡需要驱动程序,我是个门外汉,很不幸最近还没精力去学习,所以将来一段时间都是门外汉。
下面我们进入正题,来看看如何用钩子函数抓取 wave系列API,使系统播放声音时,我们的程序能够偷偷的将要发送到声卡的数据备份一份,
然后在返还给系统。
因此涉及到了几方面知识,钩子函数的编写,dll的注入,如何修改函数的地址(IAT表(Import Address Table))等。
关于钩子函数与dll编写,大家可以看一下孙鑫的VC教程最后两课,看完之后,基本就算入门了。
学了孙鑫的VC之后,我们来编写声音抓取程序还是有很大难度,可以查看如下链接的信息http://www.vckbase.com/document/viewdoc/?id=1842
这篇文章基本阐述了如何抓取声卡数据,也给我们描绘了一个比较好的使用前景,比如抓取skype的数据,实际上优酷,Google音乐等都是调用wave系列API的,
所以他们的声音均可通过这种方法进行抓取。
看了上面的链接里的文章,基本就明白了如何用钩子函数来抓取声卡数据,有两个我当时不大明白的东西在这里说明一下,
因为skype答录机是商业化软件,所以它是部分开源的,实际上已经够用了,里面有个CShareMemory类,这个类做的工作我猜想主要是内存的映射(CMapView)及互斥访问等,实际上,我们并不要管这个类,可以自己用简单的文件读写,在Hook_waveOutWrite 系列函数内将数据写入文件,得到的数据不是wave类型的,是pcm,用声音转换软件可以转换为wav及mp3格式。
CAPIHook类实现了函数地址的修改,方法是通过修改函数的IAT表,关于这方面的资料可以查看网上的一篇文章API Hook完全手册,
钩子的类型为全局钩子,设置的消息为WM_GETMESSAGE,如果自己不懒,加上一个lame_enc.dll转换为mp3也不错。
整个软件做成一个dll,然后导出一个接口,在另外一个程序内导入上面编写好的dll,就可以实现声卡的录音了,
好像没写什么,因为别人已经写得非常好了,仅做个整理,给需要做声卡录音的朋友做个参考,
我当时跌跌撞撞的花了不少时间,所以知道学技术不容易,^_^不一定正确,仅供参考,
呵呵,第一次在这上面写文章,以马斌读报里的台词结尾:到这里吧,就到这里了^_^