TNS协议解析
0. 通用规则
- 每个 Oracle 包的开始四个字节(如果启用了
large_SDU
)是标识整个包长度的字段, 不管客户端和服务端是大端序还是小端序, 这一字段都是大端序
1. Connect
基本构成
字段 | 编码方式 | 含义 |
---|---|---|
version | B2 | 客户端版本 |
version(Compatible) | B2 | 客户端支持的最低服务端版本 |
Service Options | B2 | 没用过 |
Session Data Unit Size | B2 | 客户端的 SDU 大小 |
Max Transmission Data Unit Size | B2 | 客户端的 TDU 大小 |
NT Protocol Characteristics | B2 | 没用过 |
Line Turnaround Value | B2 | 没用过 |
Value of 1 in Hardware | B2 | 客户端是大端序还是小端序, 0x0001 就是大端序 |
Length of Connect Data | B2 | 连接信息串的长度 |
Offset to Connect Data | B2 | 连接信息串在包中开始的位置 |
… | N | 没用过的数个字段 |
Connect Data | N | 客户端连接信息串, 含有 Service name 或 SID |
补充说明
-
Value of 1 in Hardware
指明了客户端发送数据所用的是大端序还是小端序, 但是当前包不会受到该字段的影响, 全部都是大端序编码. -
当
Length of Connect Data
的值超过 221 时,Connect Data
将在下一个包中发送, 而下一个包将会是一个DataPacket
这种情况发生时,
Offset to Connect Data
字段值将与当前包长度相同, 若将后续单独的Connect Data
拼接到当前包后, 则该字段相当于正常指向Connect Data
开始的位置 -
Connect Data 连接信息串示例:
(DESCRIPTION=(CONNECT_DATA=(SID=orcl10)(CID=(PROGRAM=sqlplus@kali)(HOST=kali)(USER=root)))(ADDRESS=(PROTOCOL=TCP)(HOST=10.0.72.146)(PORT=1521)))
2. Accept
基本构成
字段 | 编码方式 | 含义 |
---|---|---|
version | B2 | 服务端的版本号 |
Service Options | B2 | 没用过 |
Session Data Unit Size | B2 | 服务器的 SDU 大小 |
Maximum Transmission Data Unit Size | B2 | 服务器的 TDU 大小 |
Value of 1 in Hardware | B2 | 服务器编码是大端序还是小端序 |
Length of Accept Data | B2 | 没什么用 |
Offset to Accept Data | B2 | 没什么用 |
Connect Flags | B2 | 可以用来判断有没有 ANO_Service 协商 |
timeout | B2 | |
tick | B2 | 与 timeout 联合起来可以判断 server pool enabled |
SDU | B4 | 当服务版本在 315 及以上时启用此字段及其后续字段 |
TDU | B4 | ‘真正的’ TDU |
compression options | B1 | 与压缩协议有关的选项, 不知道启用效果 |
… | N | 不需要处理的后续字段 |
补充说明
-
对于 315 及其之后版本的服务器,后续的包会启用
large_SDU
,此时靠后的
SDU
和TDU
这两个四字节的字段才是真正的数据, 前面的会是 0后续数据包的类型是
Data
,Marker
或DataDescriptor
时, 其 Oracle 包的头部长度字段编码是四个字节. -
Value of 1 in Hardware
和ConnectPacket
中的作用范围一样, 不包含当前包 -
当前会话的
SDU
=min(TDU, SDU)
3. Redirect Packet
基本结构
字段 | 编码 | 含义 |
---|---|---|
Redirect Data Length | B2 | 重定向信息串长度 |
Redirect Data | N | 重定向信息串 |
补充说明
-
该包由服务器返回, 用于引导客户端连接真正的工作服务器.
代理支持这一特性的流程包括:
- 获取服务器指定的新的将要连接的 IP 和端口
- 新建一个代理, 目的地址指向新的 IP 和端口
- 将代理的监听地址的 IP 和端口给到协议解析层
- 协议层改写当前重定向包, 将服务器给出的地址替换成代理的监听地址
-
重定向信息串示例:
(ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.4)(PORT=2143))
4. Secure Network Service Packet
Additional Network Option Negotiation
在客户端接收到AcceptPacket之后, 可能会发出一个或多个可选的ANO协商包, 协商客户端和服务端将使用的ANO驱动
具体的包结构没有完全解析, 因为目前看到的网络数据中, 此类 Oracle 包对后续的数据没有影响
从JDBC中可以看出可以选择的服务:
服务 | 含义 |
---|---|
SupervisorService | 监管服务 |
AuthenticationService | 授权服务, 用以认证数据库用户身份 |
EncryptionService | 加密通道服务? |
DataIntergrityService | 数据完整性服务, 可能是对数据进行hash |
5. Set Protocol Packet
客户端发送的包
只包含 Accepted Versions
和 Client Platform
没有分析的必要
服务端返回的包
字段 | 编码方式 | 含义 |
---|---|---|
Version | B2 | 协议版本 |
Server Banner | string(NUL) | 服务器平台 |
server charset | B2 | 服务器字符集 小端序 |
server Flags | B1 | 和字符集转换有关系 |
server charset elements length | B2 | 字符集元素长度 小端序 |
server charset elements | N | 长度等于上一字段值乘五, jdbc中跳过了这一字段 |
NCHAR_CHARSET_LENGTH | B2 | 下一个字段的长度, 大端序 |
NCHAR_CHARSET | N | jdbc中只用到了数个位置 和多字节字符与字符串的转换有关 |
Server Compile-time Capabilities length | B1 | 下一字段长度 |
Server Compile-time Capabilities | N | 编译期权能标识 |
Server Run-time Capabilities length | B1 | 下一字段长度 |
Server Run-time Capabilities | N | 运行时权能标识 |
-
两个权能字段只在
Version
大于等于 6 时才会出现 -
Server Compile-time Capabilities
- 决定后续
Set DataType
包中的某些字段是否存在 - 影响
CLR
编码方式是否启用big chunk
- 影响
TTC Version
的取值
- 决定后续
6. Set Datatypes
编码基础
5种数据类型: B1, B2, B4, B8, PTR
3种编码类型: native, universal, LSB
-
native类型表示大端序编码
-
universal类型表示以长度为首字节的编码,默认大端序
-
LSB类型表示小端序编码,可以和universal类型结合
-
CLR
数据类型- 当数据长度小于等于 252 时,就是普通的 length-byte-preceeded 编码方式.
- 当数据长度大于 252 时,首字节是
0xFE
, 然后按照effectiveTTCC_MXIN
个字节一组使用 length-byte-preceeded 编码这一组数据, 最后一组可能不足effectiveTTCC_MXIN
个字节. 最后用0x00
结尾. effectiveTTCC_MXIN
的长度取决与Client Compile-Time Capabilities[37] & 32 && Server Compile-Time Capabilitites[37] & 32
, 启用了big chunks
时, 其值为 32767, 没有启用时为 64. 同时分组长度和结尾的0
的编码方式也会变为B4
客户端发送的包
基本结构
字段 | 编码方式 | 含义 |
---|---|---|
cliRIN | B2 | 没用过 可能是客户端的字符集 |
cliROUT | B2 | 同上 |
cliFlags | B1 | 与客户端是否启用字符串的 CLR 编码有关 |
Client Compile-time Capabilities Length | B1 | 客户端编译期权能标识长度 |
Client Compile-time Capabilities | N | 会随服务器的权能位改变 |
Client Run-time Capabilities Length | B1 | 客户端运行时权能标识长度 |
Client Run-time Capabilities | N | 客户端运行时权能标识 |
Time Zone Bytes | B11 | 可能与时区有关 没用过 |
Time Zone Version Number | B4 | 同上 |
data type representation length | B2 | 见补充 |
data type representation | N | 见补充 |
补充说明
-
Time Zone Bytes
受Client Run-time Capabilities[1] & 1
控制 -
Time Zone Version Number
受Server Compile-Time Capabilities[37] != 2
&&Client Compile-Time Capabilities[37] & 2
控制 -
data type representation length
后续字段的长度, 可能没有, 不知道哪里控制的
如果读取完上一个字段后的 offset 没有超过包长度, 那么可能存在这一字段.
可以读取后结合后续字段的单双字节编码方式, 判断是否为0x0001
或0x0101
, 来判断读取到的是后续字段还是此字段. -
data type representation
-
是一个以
0x00 00
或0x00
为分隔的 list, 用来标识各种数据类型的编码方式 -
有些客户端不会发送这一字段, 可以通过 offset 和长度来判断是否有这一字段
-
单双字节的编码方式受到
client compile-time capabilities[27]
的控制 -
取值范围和
ServerCompileTimeCapability[7]
有关ServerCompileTimeCapability[7] runtimeTypeAndReps >= 8
完整的typeAndRep >= 7
typeAndRepFor1200 <= 6
typeAndRepFor1100OrBefore 数据涵盖范围递减
-
这一 list 中的每一项的编码含义大概是:
datatype datatype encodetype 0
每一项的字段数量可能是 2, 4 或 6 , 但结尾字段的值一定是 0, 而且真正有用的只有前三个字段
datatype
所表示的类型详见oracle\jdbc\driver\TCTTIdty.java
中的以DTY
开头的一长串short
encodetype
所表示的类型详见oracle\jdbc\driver\T4CTypeRep.java
中的以REP
为开头的一长串static final byte
-
-
现已使用的
data type representation
- 代表 B2 数据类型的
0x19
, 其编码类型为0x01
0x1C
时为universal
编码,0x18
时为 2 字节编码 - 代表 B4 数据类型的
0x1D
, 其编码类型为0x01
0x1B
时为universal
编码,0x17
时为 4 字节编码 - 代表 PTR 数据类型的
0x20
, 其编码类型为0x0C
时, 长度可能是 1 或 8,0x0A
时长度为 1
- 代表 B2 数据类型的
服务端返回的包
只含有 Time Zone Bytes
和 data type representation
如果没有分析服务器编码的需求, 则没有分析的必要, 而且 JDBC 中只检查了完整性, 而没有实际使用其中的内容
7. Get Session Key
客户端发送用户名和 5 个键值对, 用以获取AUTH_SESSION_KEY
基本结构
字段 | 编码方式 | 可能出现的形式 |
---|---|---|
next sequence number | B1 | 0x01, 0x02 等, 没什么用 |
PTR | 1, 4, 8 | 在有 data type representation 时的编码方式见前一节中的描述 没有时可能是 四字节乱码加上四字节 0x00 或 八/四字节的 -2 |
user name length | B4 | 在有 data type representation 时的编码方式见前一节中的描述 没有时的编码长度为四字节 |
logon mode | B4 | 同上 |
PTR | ||
key-value list size | WORD | 在没有 data type representation 时与 PTR 同长 |
PTR PTR | ||
user name | CLR 或 纯字符串 | 根据前述的 (cliFlags & 2) == 2 判断是否为 CLR |
补充说明
-
以这个包为基础, B2, B4, B8, PTR 的编码方式基本固定下来了, 或是由
data type representation
所确定, 或是在没有data type representation
时根据已知晓的编码方式加上读取部分字节来确定. -
后续的包中的字段只需按照既有的编码方式读取对应的 B4 或 PTR 即可
8. Generic authentication call
获取了 AUTH_SESSION_KEY
之后, 正式发送加密后的密码请求认证
含有用户名以及数个键值对, 没有分析的必要
9. 含有 SQL 语句的 Data Packet
基本概念
-
Data Packet 有多种子类型, 其中包含有 SQL 语句的类型只有两类:
0x03 USER_OCI_FUNC
和0x11 PIGGYBACK_FUNC
, 以 Data Packet 的第十一个字节作为区分, 这两种类型还可以分成多种子类型, 以 Data Packet 的第十二个字节作为区分, 被称为OCI_FUNC_CODE
. -
0x03 USER_OCI_FUNC
包含 SQL 语句的子类型主要是0x035E
, 一说0x033E
也有可能包含语句, 但是0x3E
没有作为OCI_FUNC_CODE
出现在 JDBC 中. -
PIGGYBACK_FUNC
类型的数据包主要是将USER_OCI_FUNC
作为 payload 放在自身数据的后方, 所以只要去掉PIGGYBACK_FUNC
的头部就可以将其视为USER_OCI_FUNC
进行处理. -
PIGGYBACK_FUNC
可以嵌套, 即在USER_OCI_FUNC
之前可能出现多个PIGGYBACK_FUNC
的数据. -
在 JDBC 中,
PIGGYBACK_FUNC
的嵌套顺序是有顺序的, 在各个子类型出现的条件都满足的情况下, 出现的顺序应该是0x1178 0x1169 0x1187 0x119A 0x119D 0x11B0 0x11B1
, 但是目前分析过的网络数据中只会出现0x1178 + 0x1169
或0x1169
(即0x1178
以及后续的子类型出现的条件没满足, 只出现了0x1169
) 这两种类型.
0x035E
执行 SQL 语句的主体包
基本结构
字段 | 编码 | 含义 |
---|---|---|
next sequence number | B1 | 没什么用 |
options | B4 | SQL执行的命令, 指明是绑定变量语句等 |
cursor | B4 | 没什么用 |
PTR | PTR | |
sql length | WORD | SQL语句的长度 |
PTR | ||
al8i4 length | WORD | SQL语句之后的一个数组的大小 |
PTR * 2 | ||
long.MAX_VALUE | B4 | 没什么用 |
rows to fetch | WORD | 没用上 |
isPlsqlOrCall | B4 | 没用上 |
number of bind positions | PTR+WORD | 如果是绑定变量语句, 标识参数的个数 |
PTR * 5 | 很多时候是 0 1 0 1 1 (指 PTR 与 NULLPTR) | |
defCols | PTR+B4 | TTC Version >= 2 时出现 没什么用 |
RegID | B4+PTR+PTR | TTC Version >= 4 时出现 没什么用 |
RegID | PTR+B4+PTR+B4+WORD | TTC Version >= 5 时出现 没什么用 |
current rank | PTR+WORD+PTR | TTC Version >= 7 时出现 没什么用 |
null | PTR+WORD+PTR+WORD+PTR | TTC Version >= 8 时出现 没什么用 |
null | PTR+WORD | TTC Version >= 9 时出现 没什么用 |
sql | CLR 或 B1Array | SQL语句本体 |
… | N | 即使有绑定变量的参数也没法解 |
补充说明
- SQL 语句的编码方式与
user name
字段的相同
0x1178 0x1169
这两个 PIGGYBACK_FUNC
子类型的结构一样
基本结构
字段 | 编码方式 | 含义 |
---|---|---|
PTR | PTR | |
offset | 同 7 中的 key-value list size |
后续字段的个数 |
cursor ID | B4 | 没什么用, 数量有 offset 个 |
0x116B
单独出现, 无需遵守 0x1178
等的顺序
基本结构
字段 | 编码方式 |
---|---|
sididx | B4 |
sidser | B4 |
sidopc | B4 |
补充说明
PIGGYBACK_FUNC
作为出现在 USER_OCI_FUNC
之前的数据, 我们并不关注其内容, 只要能找到后续的 0x035E
数据包即可.
现有问题
-
不支持压缩协议以及SSL连接
-
没有
data type representation
的情况下, 对于PTR
字段编码方式的判断是基于经验, 不排除有例外情况, 造成后续的包全解析错误. -
绑定变量语句的参数没有解析, 如果绑定变量的参数是数据库中的结构, 比如某个表或字段的名字, 那么可能对上层语法分析造成影响
-
OCI_FUNC_CODE
涵盖并不全面, 只处理了当前能找到的网络数据中出现的类型, 有待后续追加. -
0x035E
中的options
字段对 SQL 语句的执行有一定的控制, 有待继续挖掘其含义.
参考资料
来源:CSDN
作者:vip昵称
链接:https://blog.csdn.net/weixin_40926243/article/details/104275174