Connect 流程
Client | Server | |||
1 | ------- | Connect(01) | -----> | 擷取連接配接字元串 |
2 | <----- | Resend | ------- | |
3 | ------- | Connect(01) | -----> | |
4 | <----- | Accept | ------- | 擷取協定Version |
5 | ------- | Data NetworkService(deadbeef) | -----> | 網絡參數交換 |
6 | <----- | Data NetworkService(deadbeef) | ------- | |
7 | ------- | Data SetProtocal(01) | -----> | |
8 | <----- | Data SetProtocal(01) | ------- | |
9 | ------- | Data SetDataTypes(02) | -----> | |
10 | <----- | Data SetDataTypes(02) | ------- | |
11 | ------- | Data UOCIFun(03) GetSessionKey(76) | -----> | |
12 | <----- | Data OPIParam(08) with 3 params Sessionkey,verifydata,,dbid | ------- | |
13 | ------- | Data UOCIFun(03) Generic Auth call(73) | -----> | 擷取驗證參數:使用者名,密碼在此傳輸 Username sessionkey pass |
14 | <----- | Data OPIParam(08) with 40 params | ------- | 認證結果包含 AuthDBName;dbid:AuthUserID;SessionID |
15 | ------- | Data Piggyback(11) session switch(6b) | -----> | |
16 | <----- | Data OPIParam(08) | ------- | Oracle版本号 |
認證錯誤時從14包往後,會傳回一個marker,然後用戶端會發送一個請求marker,接着服務端傳回錯誤資訊,此過程詳細參見錯誤資訊傳回這個章節
特殊資料定義
在分析32位和64位用戶端時,可以注意到不同版本用戶端再解析上出現64位feffffffffffffff和00000000000000 在32位情況下分别都被代換為01和00的情況,是以我們定義
feMagic,在32位時為0x01 64位下為0xfe ff ff ff ff ff ff ff
00Magic,在32位時為0x00 64位下為0x00 00 00 00 00 00 00 00
擷取協定版本暨協定頭解析
Connect 的Accept包是擷取TNS版本号的最佳地點,Connect過程會協商版本号,Connect過程中,client會傳輸自己支援的版本号,服務端會結合自己的情況,最終在Accept中標明一個版本号。Accept包的Package Type為2
Accept包格式
32bit | 64bit | ||
Version | 2 | 2 | 版本号 |
Service Option | 2 | 2 | Bit标志選項 |
Session Data Unit Size | 2 | 2 | 一個DataUnit最多多大,在傳輸超長包時,Data 包會被拆解成如此大小的包 |
Max Transmition Unit Size | 2 | 2 | 最大Data長度 |
Value Of 1 | 2 | 2 | 指定了服務端的Endian類型 |
Accept Data Length | 2 | 2 | |
Accept Data Offset | 2 | 2 | 指向Accept data的指針,一般直接指向結尾(包含TNS頭) |
Connect Flag0 | 1 | 1 | 标志位 |
Connect Flag1 | 1 | 1 | 标志位 |
Unknown | 8 or 17 | 8 or 17 | 未知,一般前8位元組為0 |
Service Option:
..0. .... .... .... = Broken Connect Notify
...0 .... .... .... = Packet Checksum
.... 0... .... .... = Header Checksum
.... .0.. .... .... = Full Duplex
.... ..0. .... .... = Half Duplex
.... ...0 .... .... = Don't Care
.... .... 0... .... = Don't Care
.... .... ...0 .... = Direct IO to Transpor
.... .... .... 0... = Attention Processing
.... .... .... .0.. = Can Receive Attention
.... .... .... ..0. = Can Send Attention
Connect Flag0 and flag1
...0 .... = NA services required
.... 0... = NA services linked in
.... .0.. = NA services enabled
.... ..0. = Interchange is involved
.... ...0 = NA services wanted
Accept包示例
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL90TQNdXQq1UMo1WZwx2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLyIzN3MTM0MTM5ATNwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
代碼示例
從此包中解析TNS version
--02 get tns version
if(data:byte(3)==2) then
tnsVersion=string.unpack(">I2",data:sub(7))
print("tnsVersion:"..tnsVersion)
end
擷取連接配接字元串及用戶端資訊
通過解析包Connect包可以獲得連接配接字元串,進而擷取用戶端的詳細資訊,包含用戶端程式,目前使用者,windows版本等。
Connect包格式
32bit | 64bit | ||
Version | 2 | 2 | 版本号 |
Compatible Version | 2 | 2 | 相容最低版本 |
Service Options | 2 | 2 | Bit标志選項 |
Session Data Unit Size | 2 | 2 | 一個DataUnit最多多大,在傳輸超長包時,Data 包會被拆解成如此大小的包 |
Max Transmition Unit Size | 2 | 2 | 最大Data長度 |
NT Protocol Characteristics | 2 | 2 | 網絡參數 |
Line Turn Around Value | 2 | 2 | |
Value 1 | 2 | 2 | 指定了本地的Endian類型 |
Length of Connect Data | 2 | 2 | 連接配接字元串長度 |
Offset of Connect Data | 2 | 2 | 連接配接字元串從TNS頭算的偏移量 |
Max Receivable Connect Data | 4 | 4 | |
Connection Flag0 | 1 | 1 | |
Connection Flag1 | 1 | 1 | |
Trace Across Facility item1 | 4 | 4 | |
Trace Across Facility item2 | 4 | 4 | |
Trace Unique Connection ID | 8 | 8 | |
unknown | 8 or 20 | 8 or 20 |
Service Options,Connection Flag 同Accept
NT Protocol Characteristics:
0... .... .... .... = Hangon to listener connect
.0.. .... .... .... = Confirmed release
..0. .... .... .... = TDU based IO
...0 .... .... .... = Spawner running
.... 0... .... .... = Data test
.... .0.. .... .... = Callback IO supported
.... ..0. .... .... = ASync IO Supported
.... ...0 .... .... = Packet oriented IO
.... .... 0... .... = Can grant connection to another
.... .... .0.. .... = Can handoff connection to another
.... .... ..0. .... = Generate SIGIO signal
.... .... ...0 .... = Generate SIGPIPE signal
.... .... .... 0... = Generate SIGURG signal
.... .... .... .0.. = Urgent IO supported
.... .... .... ..0. = Full duplex IO supported
.... .... .... ...0 = Test operation
包示例
示例代碼
擷取TNS版本及連接配接字元串
if(data:byte(3)==1) then
tnsVersion=string.unpack(">I2",data,7)
print("requestTnsVersion:"..tnsVersion)
local connectDataLength=string.unpack(">I2",data,23)
local connectDataOffset=string.unpack(">I2",data,25)
print("connect string:"..string.unpack("c"..connectDataLength,data,connectDataOffset-2))
end
網絡參數交換(deadbeef)Secure Network Service
通過解析包 Data Network Service 包可以獲得網絡相關參數比如servie version,其意義暫不明确,注意此包是data包,下面示例資料沒有帶data標頭
包解析示例
擷取驗證參數
通過解析包dataid 03 callid 73可以獲得使用者名,密碼hash等很多資訊
包格式
32bit | 64bit | ||
序列号 | 1 | 1 | |
可變位元組 | 16 or 20 | 44 or 48 | |
使用者名長度 | 1 | 1 | |
使用者名 | 上位元組決定 | 上位元組決定 | |
Keyvalue pairs | 變長 | 變長 | sessionkey及密碼等資料 |
可變頭
注意到使用不同用戶端連接配接不同資料庫,資料包到使用者名這裡的偏移量不同(可能原因,oracle版本,不同的用戶端)
Sqlplus11 to oracle12c | Navicat to oracle11 | 32位Navicat |
fe ff ff ff ff ff ff ff 18 00 00 00 01 01 00 00 fe ff ff ff ff ff ff ff 12 00 00 00 00 00 00 00 fe ff ff ff ff ff ff ff fe ff ff ff ff ff ff ff | fe ff ff ff ff ff ff ff 0f 00 00 00 01 01 00 00 fe ff ff ff ff ff ff ff 12 00 00 00 fe ff ff ff ff ff ff ff fe ff ff ff ff ff ff ff | 01 0f 00 00 00 01 01 00 00 01 13 00 00 00 01 01 |
一個在序号後有44個位元組,一個48個位元組,具體處理可以先跳過44個位元組看是否ff,如果是跳到48個位元組
32位情況類似,隻是将feMagic變為01,是以也有兩種情況16或20個位元組
Keyvalue對
對灰色頭部以下内容除直接跟的使用者名外,全部以keyvalue形式存在。
Key和value間存在固定4位元組未知字段,keyvalue對之間存在8位元組未知字段。
Key和value均以長度開頭,長度fe表示變長,fe後續一個位元組的長度byte并以00結尾如sessionid的值。
下面以上面的包為例進行解析:
使用者名:scott | 0863232373636f7474 |
4位元組未知字段 | 24000000 |
AUTH_SESSKEY | 0c415554485f534553534b4559 |
4位元組未知字段 | 20010000 |
value | fe403346334137413241324636443935363537434643383241304439314141383033354334334532413932313746424334384437313935343137323638374442414120423145303544373245443630413239333636454331334131444232423941303500 |
8位元組未知字段 | 100000027000000 |
AUTH_PASSWORD | 0d415554485f50415353574f5244 |
4位元組未知字段 | c0000000 |
value | 4042353343343732314336334342323537334244423535383936364541364630363844353834423034364134313945373146463430444444363537464343343742 |
... | ... |
... | ... |
... | ... |
8位元組未知字段 | 0000000030000000 |
AUTH_FAILOVER_ID | 10415554485f4641494c4f5645525f4944 |
8位元組未知字段 | 0000000000000000 |
包示例
代碼示例
擷取使用者名
--060307 get username
if(data:byte(3)==6 and data:byte(9)==3 and data:byte(10)==0x73) then
local userNamePos
--test client 32bit or 64bit
if(data:byte(12)~=0xfe)then
is64Bit=false
end
if(is64Bit) then print("64bit:true") else print("64bit:false") end
if(is64Bit) then
if(data:byte(9+2+1+43)==0xff)then userNamePos=9+2+1+44 end
if(data:byte(9+2+1+47)==0xff)then userNamePos=9+2+1+48 end
else
if(data:byte(9+2+1+15)==0xff)then userNamePos=9+2+1+16 end
if(data:byte(9+2+1+21)==0xff)then userNamePos=9+2+1+20 end
end
if(userNamePos) then
local username=string.unpack("s1",data,userNamePos)
print("username:"..username)
ngx.ctx.username=username
end
end
擷取Oracle版本号
在連接配接完成之後,用戶端會發起data 116b包,内部後續一個data DB Version(033b)請求,ThinClient 下會直接發起data 033b,請求oracle版本,版本号以data 08包形式傳回,解析其傳回包可以獲得Oracle版本号。注意傳回的data 08 包不止在這裡使用,很多指令的傳回都使用此包,此種包有這樣幾種形式,
08後直接後續字段:如版本号包
08後後續傳回字段數,再接續字段:比如認證結果傳回
擷取版本包格式如下
32bit | 64bit | ||
unused | 2 | 2 | ThinDriver 此處為1 |
Banner Length | 1 | 1 | 版本字元串長度 |
Banner | 上位元組決定 | 上位元組決定 | Oracle版本字元串 |
版本号 | 4 | 4 | 版本int表示,little endian,minor版本号和build号分别用第二個位元組的高低4bit表示 |
變長結尾 | 變長 | 變長 | 可能是1702包也可能是0901包,内容不詳 |
包示例
代碼示例
--06033b oracle version request
if(data:byte(3)==6 and data:byte(9)==3 and data:byte(10)==0x3b) then
cHeaderPos=9
end
--start to process db version request
if(cHeaderPos>1 and data:byte(cHeaderPos)==3 and data:byte(cHeaderPos+1)==0x3b) then
requestOracleVersion=true
end
--06089a get oracle version
if(data:byte(3)==6 and requestOracleVersion) then
local versionString,p=string.unpack("s1",data,12)
print("oracleVersion:"..versionString)
local minor
oracleVersion.fix,oracleVersion.subbuild,minor,oracleVersion.major=string.unpack("BBBB",data,p)
oracleVersion.minor=minor/16
oracleVersion.build=minor%16 print(oracleVersion.major..'.'..oracleVersion.minor..'.'..oracleVersion.build..'.'..oracleVersion.subbuild..'.'..oracleVersion.fix)
requestOracleVersion=false
end