使用WinDBG调试OnDO Server

2023-10-30 23:10
文章标签 使用 调试 server windbg ondo

本文主要是介绍使用WinDBG调试OnDO Server,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、问题

在OnDo服务端和PJSIP客户端配置好IPv6,发现电话机可以向服务器注册成功,但使用话机A拨打话机B时,OnDo服务器返回500 (Server Internal Error) 错误。抓的包如下:

 

通过仔细对比与IPv4下INVITE的请求,没有发现明显差异。服务器的嫌疑比较大。

二、分析

要分析这个问题,首先需要定位服务器是何时发送500错误的,为此需要跟踪服务器的执行过程。

在这里我们使用WinDBG来调试跟踪,WinDBG是微软提供的在Windows上强大的调试工具,下载页面在这里:
http://msdn.microsoft.com/en-us/windows/hardware/gg463009.aspx
如果页面过期,可以直接在MSDN中搜索 WinDBG 即可找到相关的页面。基本的调试命令这里也不解释了,文档非常详细,网络上相关的资料也非常多。

运行WinDBG,点击 File -> Attach to process ,我们要附加到OnDo Server的进程,然后才能进行调试。那么怎么找到这个进程?如果你对OnDo Server非常熟悉,知道它是一个运行于Java平台上的一个组件,那么你会非常容易找到那个合适的java.exe进程。如果不熟悉的话,我们可以这样做:
首先,打开Windows的控制台,输入这个命令:

wmic process list full

 

这个命令会详细列出正在运行的每个进程的详细信息,仔细找里面的一个(可以重定向到一个文件中,搜索ondo即可)

CommandLine="C:\Program Files\Java\jre7\bin\java" ... com.brekeke.ondo.sv ...
Description=java.exe
Handle=2368
Name=java.exe
...

在这里省略了一些大部分信息,只列出了其中几个属性,但对我们已经足够了。从CommandLine属性,我们知道我们没有看错人(进程);现在只需去找PID为2368,名称为java.exe的进程就可以了。

使用WinDBG附加到这个进程上。

然后,我们要设置断点,理想的断点是当OnDo产生500错误的时候(一定有会一个判断语句,从这个语句进入了这个分支),但这似乎并不现实,如果我们知道它如何产生的500错误,就不用如此费力的分析原因了。

所以,我们可以找到它发送500错误的时候。我们知道,在Windows平台上,网络通信最终使用的基本都是WinSock——当然也可以不是,但这种情况并不多见,我们先做这样的假设,OnDo使用的是WinSock,如果后面进行不下去了,我们再回头来分析其他情况,不过谢天谢地,这件事没有发生,我们的假设是正确的。使用WinSock,发送消息的函数并不多,就四个,下面列出了四个函数的声明:

int send(_In_  SOCKET s,_In_  const char *buf,_In_  int len,_In_  int flags
);int sendto(_In_  SOCKET s,_In_  const char *buf,_In_  int len,_In_  int flags,_In_  const struct sockaddr *to,_In_  int tolen
);int WSASend(...);    // 参数省略,详见MSDNint WSASendTo(...);  // 参数省略,详见MSDN

 更详细的信息可以查阅MSDN,WinSock Functions:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms741394%28v=vs.85%29.aspx
从MSDN我们还可以知道,这些函数都在ws2_32.dll中,因此我们就可以很方便的设置断点了(实际上我只设置了两个,而只用到了一个):

0:018> bp ws2_32!send
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\WS2_32.dll -
0:018> bp ws2_32!sendto
0:018> bl0 e 71a24c27     0001 (0001)  0:**** WS2_32!send1 e 71a22f51     0001 (0001)  0:**** WS2_32!sendto

粗体部分就是我使用的命令。

现在我们可以打电话了。我们设置的断点,会截获所有调用send和sendto的地方,因此在调试过程中,我们可以看到,OnDo首先给客户端回复100 Trying 消息,然后尝试转发INVITE消息(并失败),然后再给客户端回复500 Server Internal Error 消息。这个过程很清晰,我们可以很明确的看到,转发INVITE消息的sendto调用,返回值是-1 (0xFFFFFFFF),也就是SOCKET_ERROR。

这个事情让人很疑惑,我的两个话机是在同一个ONT上的,IP地址是一样的,唯一不同的就是注册的号码和绑定的端口号,sendto显然不会涉及到注册的号码,那么为什么给同一个IP地址的两个端口发送数据会导致两种截然不同的结果呢?我们还记得两部话机的注册都是好的,500错误也可以收到,那么是什么导致这样的差异呢?对此,我们可以详细比较一下转发INVITE和发送100通知(或者500错误)的两次sendto调用的参数。

下图是发送100 Trying 消息时调用 sendto() 的堆栈(从esp寄存器看就可以了)。

 

下图是转发INVITE 消息时调用 sendto() 的堆栈。

 

这样看还不够明显。我们知道 sendto() 的 6 个参数,因此逐个对比一下:

参数

100 Trying

INVITE

Socket

60 0d 00 00

60 0d 00 00

Buffer

44 f1 54 03

44 f1 64 03

Buffer length

81 01 00 00

89 04 00 00

Flags

00 00 00 00

00 00 00 00

Socket address

28 f1 54 03

28 f1 64 03

Socket address length

1c 00 00 00

1c 00 00 00

第二、三、四个参数基本可以忽略。我们看到,两次发送,连套接字使用的都是相同的。似乎INVITE更加没有理由发送失败了。现在唯一不同就是发送的目的地 (struct sockaddr 结构) 了,我们再到那块内存中继续寻找线索。

这个内存在截图中已经体现出来了(就是在发送的buffer前面的28个字节)。这个结构的定义是这样的:

struct in6_addr {union {u_char  Byte[16];u_short Word[8];} u;
};struct sockaddr_in6 {short   sin6_family;         // 2u_short sin6_port;           // 2u_long  sin6_flowinfo;       // 4struct  in6_addr sin6_addr; // 16u_long  sin6_scope_id;       // 4   
};

应该明确一下,IPv4和IPv6使用的结构是不同的,这也是sendto() 最后一个参数的用途,我们这里自然只需关心IPv6的结构。为了更加明显的进行对比,请看下表:

成员

100 Trying

INVITE

sin6_family

17 00

17 00

sin6_port

13 d8

13 da

sin6_flowinfo

00 00 00 00

00 00 00 00

sin6_addr

fe c0 00 00 00 00 00 00

c3 0c ab ff 13 6f 22 1c

fe c0 00 00 00 00 00 00

c3 0c ab ff 13 6f 22 1c

sin6_scope_id

01 00 00 00

00 00 00 00

其中差异已经用红蓝两种颜色表示出来了。前面我说过,我的两个话机是在同一个ONT上,使用不同的端口注册的,所以他们共享同一个IP。从这里你可以明确的看出两个端口分别是5080(0x13d8)和5082(0x13da),因此这个差异是预料之中的。那么剩下的就只有 sin6_scope_id 了。为什么一个是1,另一个是0呢?其实首先应该问,是这个差异导致的 sendto() 失败进而服务器返回 500 错误吗?答案是肯定的。当我们人为的把这个0改为1的时候,奇迹(其实不是奇迹,而是我们预期的现象)出现了,INVITE发送成功,另一个话机响铃了。现在要问,为什么一个是1,另一个是0呢?我不知道。是的,我不知道。同样的配置,在Windows 7 系统上完全没有问题,而在 Windows XP 上就戏剧性的出现了上面的一幕。在OnDO的配置上,Configuration -> System -> Network,我们只设置了IPv6地址,而没有设置 scope 。

 

因此,OnDO在打算从这个 interface 发送 INVITE 请求时,使用了某种方法获取 scope ID,而显然它没有获取到(或者获取到了错误的值),后面的事情我们都知道了。因此,假如我们在这里明确指定 scope :

 

结果就正确了,所有的数据包都正常发送,电话可以通了。友情提示,这里的 scope 可以在命令行输入 ipconfig 查看,请不要猜测。

到这里,问题已经解决了。现在我依然很诧异OnDO在Windows XP和Windows 7系统的表现上的差异究竟来源于何处。

三、结论

归根结底,还是OnDO 服务器的配置问题,至少在 Windows XP 系统上,如果使用IPv6,需要把 scope 同时填写到地址上。这一点在Brekeke官方的Wiki上并没有体现(或许是他隐含的意思?),这就导致了我们兜了一个大圈。

转载于:https://www.cnblogs.com/zhangbaoqiang/p/3145326.html

这篇关于使用WinDBG调试OnDO Server的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/311166

相关文章

SQL Server配置管理器无法打开的四种解决方法

《SQLServer配置管理器无法打开的四种解决方法》本文总结了SQLServer配置管理器无法打开的四种解决方法,文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录方法一:桌面图标进入方法二:运行窗口进入检查版本号对照表php方法三:查找文件路径方法四:检查 S

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

Python内置函数之classmethod函数使用详解

《Python内置函数之classmethod函数使用详解》:本文主要介绍Python内置函数之classmethod函数使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 类方法定义与基本语法2. 类方法 vs 实例方法 vs 静态方法3. 核心特性与用法(1编程客

Linux中压缩、网络传输与系统监控工具的使用完整指南

《Linux中压缩、网络传输与系统监控工具的使用完整指南》在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛,下面小编就来和大家详细介绍一下它... 目录引言一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作2.

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

ModelMapper基本使用和常见场景示例详解

《ModelMapper基本使用和常见场景示例详解》ModelMapper是Java对象映射库,支持自动映射、自定义规则、集合转换及高级配置(如匹配策略、转换器),可集成SpringBoot,减少样板... 目录1. 添加依赖2. 基本用法示例:简单对象映射3. 自定义映射规则4. 集合映射5. 高级配置匹

Spring 框架之Springfox使用详解

《Spring框架之Springfox使用详解》Springfox是Spring框架的API文档工具,集成Swagger规范,自动生成文档并支持多语言/版本,模块化设计便于扩展,但存在版本兼容性、性... 目录核心功能工作原理模块化设计使用示例注意事项优缺点优点缺点总结适用场景建议总结Springfox 是

嵌入式数据库SQLite 3配置使用讲解

《嵌入式数据库SQLite3配置使用讲解》本文强调嵌入式项目中SQLite3数据库的重要性,因其零配置、轻量级、跨平台及事务处理特性,可保障数据溯源与责任明确,详细讲解安装配置、基础语法及SQLit... 目录0、惨痛教训1、SQLite3环境配置(1)、下载安装SQLite库(2)、解压下载的文件(3)、