使用 MS SQL Server 进行服务器端查询拦截

逆向工程 视窗 dll mssql
2021-06-15 02:26:27

我正在研究拦截到达 SQL Server 2008 进程的查询。

SQLOS 架构分为以下系统 DLL:

  • sqlmin.dll:存储、复制、安全功能等。
  • sqllang.dll : TransactSQL 查询执行引擎、表达式求值等。
  • sqldk.dll:任务调度和分派、工作线程创建、消息循环等。

SQLSERVR服务进程通过sqlboot.dllsqldk.dll实例化 SQLOS 组件,工作线程通过服务器中选择的连接方法(TCP/IP、本地共享内存或命名管道)接收查询。

我已经调试了搜索文本查询的 sqlservr.exe 进程地址空间。查询字符串似乎是可读的,但是我找不到可以在查询进入 SQLOS 调度程序时拦截查询的点。

目前无法选择侦听管道或 TCP/IP;我想在更高级别注入,最好是在 SQLOS 组件级别。

知道从哪里开始调查吗?

4个回答

对于周日下午来说,这似乎是一个有趣的项目,所以我试了一下。为了直接切入正题,这里是 SQL Server 中一个函数的调用堆栈,该函数解析然后执行查询(取自在 Windows 7 SP1 32 位上运行的 SQL Server 2008 R2 的地址和偏移量):

0x7814500a msvcr80.i386!memcpy+0x5a
0x013aa370 sqlservr!CWCharStream::CwchGetWChars+0x5c
0x013a9db5 sqlservr!CSQLStrings::CbGetChars+0x35
0x012ffa50 sqlservr!CParser::FillBuffer+0x3d
0x0138bbfd sqlservr!CParser::CParser+0x3c8
0x01352e96 sqlservr!sqlpars+0x7b
0x013530f2 sqlservr!CSQLSource::FParse+0x16d
0x013531ed sqlservr!CSQLSource::FParse+0x268
0x012ff9e8 sqlservr!`string'+0x3c
0x015894b8 sqlservr!CSQLSource::Execute+0x2c8
0x0158ad31 sqlservr!process_request+0x2ac
0x0158a328 sqlservr!process_commands+0x15f
0x015cf8b4 sqlservr!SOS_Task::Param::Execute+0xdd
0x015cf9ea sqlservr!SOS_Scheduler::RunTask+0xb4
0x015cf575 sqlservr!SOS_Scheduler::IsShrinkWorkersNecessary+0x48
0x77f06854 ntdll!ZwSignalAndWaitForSingleObject+0xc
0x77e479e2 kernel32!SignalObjectAndWait+0x82

基于此,您可能想要仔细查看CSQLSource该类,尤其是它的Execute方法。

有了这些信息,我还能够挖掘出Microsoft 某人关于如何从 SQL Server 的内存转储中提取查询字符串的几篇博文那篇文章似乎证实我们走在正确的轨道上,并为您提供了一个插入的地方和一种提取查询字符串的方法。

方法

我觉得使用某种形式的动态二进制仪器 (DBI) 最容易解决这个问题;由于我们怀疑查询字符串将在 SQL Server 进程中的某处进行处理,因此我们可以查看进程进行的内存读取和写入,搜索读取或写入查询字符串的点。然后我们可以在那个时候转储调用堆栈,看看出现了哪些有趣的地址,并将它们映射回符号(因为,正如 Rolf 指出的,SQL Server 有可用的调试符号)。真的就是这么简单!

当然,诀窍是周围有一些东西可以让您轻松地对流程进行检测。我使用基于QEMU的(希望即将发布的)全系统动态分析框架解决了这个问题这让我避免了让 SQL Server 在例如PIN下运行所涉及的任何不愉快因为该框架包括记录和重放支持,所以我也不必担心我的检测会减慢服务器进程。获得调用堆栈后,我使用PDBParse来获取函数名称。

仅嗅探流量......很容易

如果您只想嗅探流量,您可以使用WireShark附带的 TDS 协议嗅探器

让懒惰引导你——懒惰是逆向者的朋友

目前无法选择侦听管道或 TCP/IP;我想在更高级别注入,最好是在 SQLOS 组件级别。

我不知道为什么当所有信息都可用并且您需要做的就是将拼图拼凑在一起时,您为什么坚持以特定方式执行此操作。这似乎是最简单、最快的方法——简而言之:最懒惰的方法。除了 TCP/IP更高级别,因为如果您可以劫持SQL 服务器的 IP/名称并在两者之间放置一个“代理”,您甚至可以在它到达实际 SQL 服务器机器之前拦截它如何高水平的你要吗?您所坚持的实际上是深入了解 MS SQL Server 的较低级别。

MS SQL Server 使用文档化协议并使用LSP,您应该/将能够嗅探、拦截甚至操纵该流量。据我回忆,LSP 在应用程序的进程空间内运行,它们正在过滤其流量。从字面上看,您可以将它们视为临时的应用程序级防火墙。

或者 - 无论如何可能是更好的选择 - 您可以编写基于现有和免费FreeTDS(在 LGPL 下许可)的代理tdspool计划将是开始这项努力的好时机。是的,这应该适用于实际拦截,而不仅仅是嗅探转发的流量。您可以使用库 (FreeTDS) 对查询进行解码和重新编码。显然,该库也将是在您的 LSP 中使用的库。

尽管我安装了 MS SQL Server 2008 并在 IDA Pro 中简要地查看了它,但我将节省时间来详细了解反汇编。Brendan 的回答提供了一个很好的概述,即使我不同意这种过于复杂的方法,但可以使用更简单的方法。但是,你(Hernán)要求它。

总的来说,我想说的是,像这样的问题是特定于应用程序的。因此,尽管用户百老汇对他的回答投了反对票,但如果我不知道针对该问题的任何好的、特殊的解决方案,我会给出完全相同的建议。您将要做的是观察进入过程的数据,然后在整个程序中复制和操作数据时进行跟踪。由于调试符号可用于 SQL Server,因此此任务将比一般情况更容易。你有没有尝试过这些方面的事情?比如说,在 SQL Server 上下文中的网络接收类型函数上设置断点,在通过网络传入的数据上设置硬件 RW 断点,然后观察数据如何在大量代码中移动?

我对该目标没有任何具体了解,但我可能采取的方法是通过管道、tcp 和共享内存发送相同的消息,并使用 pin 跟踪它们,寻找基本块的命中与所有收敛的位置traces 应该为您提供一些微调良好注入点的起点。