欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

《Dive into Windbg系列》AudioSrv音频服务故障

来源:本站整理 作者:佚名 时间:2019-04-15 TAG: 我要投稿

起因
最近换了HDMI显示器后,提示正在寻找音频设备,随后系统没声音了。右下角喇叭出现红叉,自动修复提示音频服务未响应,重装音频驱动也没用,系统是Windows 10 1803 64位。

多次尝试修复无果,于是打开调试器一探究竟。
 
寻找突破口
从何处入手?这不由得让我想起《How to solve it》一书:问题是什么?之间的关联?有哪些已知线索?
系统的音频面板中列出了本机所有接口,操作一番发现设置为默认的功能不生效。

我意识到这可能跟音频服务无响应有关,于是决定从这个点入手。
可以看到设置默认选项是一个PopMenu,因此打算先找到该菜单的响应函数,进而分析后续的代码实现。打开procexp,查找该窗口对应的进程,发现是rundll32,如下:
  "C:windowssystem32rundll32.exe" Shell32.dll,Control_RunDLL mmsys.cpl,,sounds
mmsys.cpl是一个控制面板程序,CPL是PE文件,导出了CPlApplet函数,该函数是程序的逻辑入口,原型如下:
__declspec(dllexport) long __stdcall CPlApplet(HWND hwndCPL,UINT uMsg,LPARAM lParam1,LPARAM lParam2);
为了找到PopMenu窗口的消息处理过程(WndProc),首先通过spy++找到菜单所属窗口的句柄wnd,接着写一段代码注入到rundll32进程中获取:
LONG_PTR ptr = NULL;
HWND wnd = ***;
//https://blogs.msdn.microsoft.com/oldnewthing/20031201-00/?p=41673
if (IsWindowUnicode(wnd))
  ptr = GetWindowLongPtrW((HWND)wnd, GWLP_WNDPROC);
else
  ptr = GetWindowLongPtrA((HWND)wnd, GWLP_WNDPROC);
找到WndProc是ntdll!NtdllDialogWndProc_W,接着就需要条件断点,PopMenu的菜单响应是WM_COMMAND(0x0111)消息,WndProc原型如下:
LRESULT CALLBACK WindowProc(HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
);
可知rcx是窗口句柄,rdx是消息ID,因此设置条件断点如下:
bp ntdll!NtdllDialogWndProc_W ".if(@rcx==句柄 and @rdx==0x0111){.printf "%x %xn",@rcx,@rdx;.echo}.else{gc}"

中断下来之后使用pc命令找到对应的调用,根据符号名能大致知道函数的功能,最后找到PolicyConfigHelper::SetDefaultEndpoint函数,调用栈如下所示:
00 audioses!PolicyConfigHelper::SetDefaultEndpoint
01 audioses!CPolicyConfigClient::SetDefaultEndpointForPolicy
02 mmsys!CEndpoint::MakeDefault
03 mmsys!CPageDevices::ProcessWindowMessage
04 mmsys!CDevicesPageRender::ProcessWindowMessage
05 mmsys!ATL::CDialogImplBaseTATL::CWindow>::DialogProc
06 atlthunk!AtlThunk_0x01
07 USER32!UserCallDlgProcCheckWow
08 USER32!DefDlgProcWorker
09 USER32!DefDlgProcW
10 ntdll!NtdllDialogWndProc_W
uf /c 查看audioses!PolicyConfigHelper::SetDefaultEndpoint调用函数如下:
0:000> uf /c audioses!PolicyConfigHelper::SetDefaultEndpoint
audioses!PolicyConfigHelper::SetDefaultEndpoint (00007ffc`6c3adc7c)
    call to audioses!GetAudioServerBindingHandle (00007ffc`6c387be4)
    call to RPCRT4!NdrClientCall3 (00007ffc`94e706f0)
    call to audioses!FreeAudioServerBindingHandle (00007ffc`6c387b78)
    call to audioses!WPP_SF_D (00007ffc`6c3643d4)
 
RPC调试方法
查看GetAudioServerBindingHandle函数:
audioses!GetAudioServerBindingHandle (00007fff`d2c07be4)
    call to RPCRT4!RpcStringBindingComposeW (00007ff8`07882e60)
    call to RPCRT4!RpcBindingFromStringBindingW (00007ff8`0788d8b0)
    call to RPCRT4!RpcStringFreeW (00007ff8`0787ab40)
可知在连接RPC服务端,得到端口句柄。接下来的NdrClientCall3便是执行RPC客户端调用。
RPC全称Remote Procedure Call(远程过程调用),主要是实现客户端的函数在服务端上下文调用,对客户端来说像在调用本地函数一样,为此这里会涉及几个点:
函数原型一致
序列化/反序列化
同步异步
数据交换
内存分配
异常处理
注册发现
传输方式
...
关于RPC,可以讲很多东西,因篇幅有限,我将重心放在Windows的RPC,同时讲一些调试技巧。对RPC感兴趣的可以去看看gRPC、brpc(有很多研究资料)、Thrift,以及一些序列化协议(pb、json、mp)等。
Windows对RPC使用无处不在,COM的跨进程通信便是用的RPC,还有许多服务都提供了RPC调用接口,例如LSA、NetLogon等等。
读者需要理清COM、RPC、LPC/ALPC之间的关联,这里可以分三个层次:
COM -- ole*.dll、combase.dll
RPC -- rpcrt4.dll
LPC/ALPC -- ntdll!Zw*Port/ntdll!ZwAlpc*
COM在垮进程通信时会调用到RPC,RPC在本地调用时会用到LPC(本地过程调用Local Procedure Call)(也有可能是Socket/NamedPipe,大部分应该都是LPC,因为效率最高),LPC是NT旧时代的产物,Vista之后LPC升级成了ALPC,A是Advanced高级的意思,ALPC通信速度、安全性、代码规范,可伸缩性都有提升,这些概念可以参考Windows Internals。

[1] [2] [3]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载