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
|
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
从此包中解析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版本等。
|
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
通过解析包 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
在连接完成之后,客户端会发起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