本系列重點分析TNS 314下的客戶端與服務端之間的通訊,通過抓包分析,查看在不同客戶端,不同服務端情況下傳輸方式的不同,嘗試還原其協議細節,實現對協議中一些關鍵內容的解析,如登錄用戶名,協議版本,oracle版本,sql命令,同時給出示例LUA代碼。為了分析不同客戶端架構,本系列使用了兩類客戶端3... ...
前言
Oracle 客戶端與服務端採用TNS作為其數據交換協議。TNS全稱Transparent Network Substrate,是與Oracle資料庫伺服器通訊的專有協議,該協議為Oracle內部協議,不向外界公開,在此之前,已經有一些反向工程的實踐對各個版本的TNS進行解析,比如wireshark就有專門的TNS分析工具,中文的協議解析可參見《ORACLE協議分析》本系列基礎介紹中關於TNS包基礎格式,及連接包等均沿用wireshark提供的格式。本系列重點分析TNS 314下的客戶端與服務端之間的通訊,通過抓包分析,查看在不同客戶端,不同服務端情況下傳輸方式的不同,嘗試還原其協議細節,實現對協議中一些關鍵內容的解析,如登錄用戶名,協議版本,oracle版本,sql命令,同時給出示例LUA代碼。為了分析不同客戶端架構,本系列使用了兩類客戶端32位與64位客戶端進行測試,同時重點使用了多個廠商的不同客戶端(Navicat、PLSQL、SQLPlus)同時也兼顧分析了OJDBC Thin Client的情況。服務端採用11g和12c兩個版本。本文主要分析連接建立,身份驗證、命令傳輸和返回、以及錯誤信息返回的過程。
方法及工具
主要採用wireshark對客戶端與Oracle間的通訊進行抓包分析。
客戶端: |
服務端 |
Navicat Premium 15 64bit |
Oracle 11g 64bit Linux |
Navicat Premius 12 32bit |
Oracle 12c 64bit windows |
PLSQL 11.2 64bit |
|
SQLPlus 11.2 64bit |
|
OJDBC8(Thin Client) |
|
分析過程中關於包類型定義等參考wireshark的tns 解析器代碼。
代碼示例說明
代碼示例用lua寫成,可以在openresty15 64bit window或linux版本下運行
其中從socket流中解碼用到了string.unpack 和pack 方法是純lua開源實現,是對c語言 lua 擴展lpack 的純lua模擬
系列目錄
協議介紹
Transparent Network Substrate顧名思義是對傳輸層協議無關,根據Oracle的介紹:TNS底層支持TCP,SSL TP,SDP,named pipeline等協議。在OSI七層協議體系中,TNS屬於會話層協議
詳細的介紹可參考
https://docs.oracle.com/cd/B28359_01/network.111/b28316/architecture.htm#NETAG004
Oracle版本與TNS協議版本對應關係
Oracle 版本 |
TNS版本 |
11gr2 12c |
tns314 |
10g |
313 |
9i |
312 |
X |
311 |
8i |
310 |
不同driver的差異
Oracle Client調用服務端,最終實現模式主要是 OCI和ThinDriver。
- OCI是C 語言的lib,和平臺相關,有linux和windows 版本;
- ThinDriver是純Java的包,與平臺無關;
很多人反映反編譯Java代碼看到的情況和用plsql等工具調用實現不同,大概率是此原因,但無論Navicat,SQLPlus,PLSQL都走的OCI。相應JDBC有兩種
- JDBC OCI Driver 走OCI調用,
- JDBC Thin Driver走 純Java實現
ThinDriver的實現與OCI的實現從抓包上看,在連接建立和認證過程差距不大,但Data包差距很大,無論Endian模式,還是具體數據類型封裝格式都有較大差異,在ThinDriver的實現中,很少未見出現Piggy Command的情況,所有非0或大於一位的byte或者int變數都會嚴格前序長度欄位。
不同客戶端實現的差異
相同位數(都是64位或者都是32位)的不同客戶端的實現在抓包分析時也不太相同,大概表現有兩個方面,一個是流程上的不同,一個是數據上的不同,我們以執行select語句為例,PLSQL和Navicat在流程上稍有差別,下圖是Navicat Premium15 執行的流程
1 |
------- |
Data Piggyback(11) Cursor Close All(69) 註意此處也有可能是 03 5e |
-----> |
具體語句 |
2 |
<----- |
Data DescribeInfo(10) 17 |
------- |
返回列 |
3 |
------- |
Data UOCIFun(03) ExecuteARow(04) |
-----> |
|
4 |
<----- |
Data ReturnStatus(04) |
------- |
|
5 |
------- |
Data UOCIFun(03) FetchARow(05) |
-----> |
獲取其他值 |
6 |
<----- |
Data RowTransferHeader(06) 01 |
------- |
返回值 |
而PLSQL中沒有中間3和4的部分。
數據上的不同有的時候是一些數據長度,有的時候是一些設置位上的差異,比如對於select 的Piggycommand(pagekage type 0x6 DataId=0x11 CallID=0x69)
這個包內容在Sqlplus,plsql,navicat上不僅內容,長度也就有差異
語句 |
Plsql |
Sqlplus |
Navicat |
Select |
fe ff ff ff ff ff ff ff 01 00 00 00 04 00 00 00 |
fe ff ff ff ff ff ff ff 01 00 00 00 00 00 00 00 05 00 00 00 當沒有輸入;05會變03 |
fe ff ff ff ff ff ff ff 01 00 00 00 05 00 00 00 |
不同客戶端位數間差異
32位客戶端和64位客戶端也有不同,比如32位客戶端中所有0xfe ff ff ff ff ff ff ff 全部以其補碼01代替。但64bit 的ThinClient也有此情況,所以很難說這些現象是否完全由位數造成,這會導致許多協議中許多部分的長度根據位數不同有一定區別。文中凡協議均會標註出不同客戶端位數下的長度,若未單獨標註,則說明二者無不同,例如Buddle execute Command命令格式:
|
32bit |
64bit |
|
序列號 |
1 |
1 |
|
Piggy command |
9 or 13 |
16 or 20 |
|
Buddle execute command 035e |
變長 |
變長 |
|