腾讯QQ空间g_tk算法[附C#实现代码]
今天张筱祥写一个QQ空间相关工具的时候,有个参数是g_tk,找了半天也没发现是在哪里生成的,百度了一下找到一个朋友写的解密技巧,转载到张筱祥博客记录下。
原文地址:http://www.codeages.com/forum.php?mod=viewthread&tid=43004&extra=page%3D1
原文详细内容:
在百度搜索g_tk,只得到几个可怜而且不完整的答案,易语言的也有人写了个模块,却一直不愿意公开算法,不知道出于什么目的,不过我顺便鄙视一下那些不愿意分享代码而且还把这些鸡肋的知识当宝的人。那我首次在源始时代公开一下QQ空间的g_tk算法。其实g_tk只是QQ空间对日志进行操作的时候,所采取的一套安全机制,如果g_tk字符串的值不对的话,请求是没有办法提交的,因此,很多刚刚涉及HTTP协议技术的人想对QQ空间这尊大佛动手脚的话,只能望而却步。下面我以VB为例,在这里详解一下g_tk的计算方法。
其实g_tk校验是通过skey值来算出来的,弄过QQ登录的人可能都知道,在登录成功之后,cookies里都会返回skey值,通常是以@开头,并且带有一串看似无规则的大小写字母混合,总共10位。下面我们先来抓包看看,g_tk到底用在了哪里,我们以转载日志为例来抓包,上图: 
完整数据包内容如下:
- POST /cgi-bin/blognew/blog_quote HTTP/1.1
- Accept: */*
- Accept-Language: zh-cn
- Referer: http://b.qzone.qq.com/proxy.html
- If-Modified-Since: 0
- Content-Type: application/x-www-form-urlencoded
- Accept-Encoding: gzip, deflate
- User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Tablet PC 2.0)
- Host: b.qzone.qq.com
- Content-Length: 65
- Connection: Keep-Alive
- Cache-Control: no-cache
- Cookie: pt2gguin=o0138001655; ptcz=0b25a27219dd08bcfe38fc85365593dadb1a2a99cac9f1abfd5fb31a7052f89b; pvid=6724688319; flv=10.0; adid=138001655; adSP=GHTsOtSHTIJdDIr9+GXVoaFY59pet/LONpbU1rA0yPY=_837_326830_1290874683_; adVer=3121; ac=1,030,006; ptui_qstatus=2; uin=o0138001655; skey=@sZmfEEBdt; ptisp=ctc; ssid=s8226120880; login_time=B46BD5B3A93F9EC5226847DB4AE9A71589641475FCCEBBC9; __Q_w_s__appDataSeed=1; randomSeed=220115
- uin=138001655&fromuin=715746717&blogid=1286714133&g_tk=1423927145
我们可以看到,数据包主体部分最后一个参数就是g_tk值,一般是一串数字。那这个值到底怎么算出来的呢?
因为我们在网页登录QQ的时候,腾讯都会通过cookies里的skey值来计算,用js来算。既然在运算的时候执行了js脚本,那么我们就可以在抓包中获得。那g_tk是通过什么算法算出来的?其实很简单,当我们得到skey后,循环取单字符的二进制并取左值.累加之后就得到后面的g_tk值了,这听上去很复杂,不过算法不用我们自己写,我们只需要执行在腾讯网页登录的时候所执行的那个js脚本就可以了。当然,js不能直接调用,不过既然我写了这篇文章,就已经是有备而来的,js算法我已经整理并写了一个最简单的,代码如下:
- function getGTK(str){
- var hash = 5381;
- for(var i = 0, len = str.length; i < len; ++i){
- hash += (hash << 5) + str.charAt(i).charCodeAt();
- }
- return hash & 0x7fffffff;
- }
复制代码
那么我们现在还有两个问题没有解决:
1.如何获取登录后的cookies?
2.如何在VB中执行js代码并得到返回值?
上面两个问题其实到了你们手里,我相信也不会是问题了,下面我再通过代码以及讲解,来剖析并解决这两个所谓的问题。对于HTTP数据包POST/GET,相信看这篇文章的人应该都懂得吧,否则你看了也没用,那么我们可以设计一个登录程序,并在登录之后获取cookies中的skey值,并计算出g_tk。下面设计个登录界面: 
主界面代码如下:(frmLogin.frm)
- ‘———————————————————————————————-
- ‘ – 腾讯QQ空间g_tk算法
- ‘ – 作者:泡面 (QQ138001655 email:admin@mafom.com)
- ‘ – 日期:2010/11/28
- ‘———————————————————————————————-
- ‘ – 本源码来自源始时代(http://www.codeages.com),转载时请保留此信息!
- ‘ – 本程序仅供学习、交流用,下载后请于24小时内删除,使用所带来的后果概不负责!
- ‘———————————————————————————————-
- Option Explicit
- ‘wininet提供的API函数,用于获取cookies
- Private Declare Function InternetGetCookie Lib "wininet.dll" Alias "InternetGetCookieA" (ByVal lpszUrlName As String, ByVal lpszCookieName As String, ByVal lpszCookieData As String, lpdwSize As Long) As Boolean
- Private Sub cmdCancel_Click()
- End
- End Sub
- Private Sub cmdLogin_Click()
- On Error GoTo hErr
- If Len(txtQQNumber.Text) = 0 Then
- MsgBox "请输入QQ号码!", vbInformation, "提示"
- txtQQNumber.SetFocus
- Exit Sub
- End If
- If Len(txtPassword.Text) = 0 Then
- MsgBox "请输入QQ密码!", vbInformation, "提示"
- txtPassword.SetFocus
- Exit Sub
- End If
- If Len(txtVlCode.Text) = 0 Then
- MsgBox "请输入验证码!", vbInformation, "提示"
- txtVlCode.SetFocus
- Exit Sub
- End If
- cmdLogin.Enabled = False
- ScriptControl1.Language = "Jscript"
- ScriptControl1.Timeout = -1
- ScriptControl1.AddCode txtVarHexcase.Text
- Dim QQPass As String
- Dim retString As String
- ‘对QQ密码进行加密,否则服务器不会通过
- QQPass = ScriptControl1.Run("md5", ScriptControl1.Run("md5_3", txtPassword.Text) + UCase(txtVlCode.Text))
- ‘发送登录请求
- Inet1.Execute "http://ptlogin2.qq.com/login", "POST", "u=" & txtQQNumber.Text & "&p=" & QQPass & "&verifycode=" & txtVlCode.Text & "&aid=15000101&u1=http%3A%2F%2Fimgcache.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone&fp=loginerroralert&h=1&ptredirect=1&ptlang=0&from_ui=1&dumy=", "CONTENT-TYPE : application/x-www-form-urlencoded"
- Do While Inet1.StillExecuting
- DoEvents
- Loop
- ‘取得返回数据
- Dim BinBuff() As Byte
- BinBuff() = Inet1.GetChunk(0, icByteArray)
- retString = UTF8_Decode(BinBuff())
- ‘判断登录状态
- If InStr(retString, "QQ社区登录") Then
- MsgBox "登录成功!", vbInformation, "提示"
- Dim nLen As Long
- Dim sBuff As String * 1024
- nLen = 1024
- ‘获取cookies
- InternetGetCookie "http://ptlogin2.qq.com/login", vbNullString, sBuff, nLen
- ‘获取skey值
- Dim skey As String
- Dim sPos As Long
- sPos = InStr(sBuff, "skey=@")
- If sPos <> 0 Then
- skey = Mid(sBuff, sPos + 5, 10)
- MsgBox "从cookies获取到的skey值:" & skey
- End If
- ‘执行js脚本,计算g_tk值
- Dim js(6) As String
- Dim g_tk As String
- js(0) = "function getGTK(str){" & vbCrLf
- js(1) = "var hash = 5381;" & vbCrLf
- js(2) = "for(var i = 0, len = str.length; i < len; ++i){" & vbCrLf
- js(3) = " hash += (hash << 5) + str.charAt(i).charCodeAt();" & vbCrLf
- js(4) = "}" & vbCrLf
- js(5) = " return hash & 0x7fffffff;" & vbCrLf
- js(6) = "}"
- ScriptControl1.AddCode js(0) & js(1) & js(2) & js(3) & js(4) & js(5) & js(6)
- g_tk = ScriptControl1.Run("getGTK", skey)
- MsgBox "计算出的g_tk值:" & g_tk
- Else
- If InStr(retString, "您输入的验证码有误,请重试。") <> 0 Then
- MsgBox "您输入的验证码有误,请重试!", vbExclamation, "提示"
- ElseIf InStr(retString, "您输入的密码有误,请重试。") <> 0 Then
- MsgBox "您输入的密码有误,请重试!", vbExclamation, "提示"
- ElseIf InStr(retString, "您的QQ号码存在安全隐患") <> 0 Then
- MsgBox "您的QQ号码存在安全隐患!", vbExclamation, "提示"
- Else
- MsgBox "登录失败,请检查您的密码是否正确!", vbExclamation, "提示"
- End If
- End If
- cmdLogin.Enabled = True
- Exit Sub
- hErr:
- MsgBox "错误:" & Err.Number & vbCrLf & vbCrLf & Err.Description, vbExclamation, "错误"
- Exit Sub
- End Sub
- ‘获取验证码
- Sub GetCode()
- On Error Resume Next
- Dim Buff() As Byte
- ‘腾讯最新登录接口,验证码已经升级为5位,请求验证码的时候必须要加上QQ号码
- Inet1.URL = "http://captcha.qq.com/getimage?aid=46000101&r=0.03652396363445809&uin=" & txtQQNumber.Text & "&vc_type=063620256136860e997ba2ca06c3c10a43c1f346db8e9d98"
- Buff() = Inet1.OpenURL(, icByteArray)
- With picVlCode
- .Picture = PictureFromBits(Buff()) ‘直接得到Picture对象
- .PaintPicture .Picture, 0, 0, .Width, .Height, 0, 0, .ScaleWidth, .ScaleHeight
- End With
- End Sub
- Private Sub Form_Load()
- Me.Show
- GetCode
- txtQQNumber.SetFocus
- End Sub
模块代码:(mdlALG)
- ‘———————————————————————————————-
- ‘ – 腾讯QQ空间g_tk算法
- ‘ – 作者:泡面 (QQ138001655 email:admin@mafom.com)
- ‘ – 日期:2010/11/28
- ‘———————————————————————————————-
- ‘ – 本源码来自源始时代(http://www.codeages.com),转载时请保留此信息!
- ‘ – 本程序仅供学习、交流用,下载后请于24小时内删除,使用所带来的后果概不负责!
- ‘———————————————————————————————-
- ‘二进制转UTF8
- Declare Function MultiByteToWideChar _
- Lib "kernel32" (ByVal CodePage As Long, _
- ByVal dwFlags As Long, _
- ByVal lpMultiByteStr As Long, _
- ByVal cchMultiByte As Long, _
- ByVal lpWideCharStr As Long, _
- ByVal cchWideChar As Long) As Long
- Public Enum CBoolean
- CFalse = 0
- CTrue = 1
- End Enum
- Private Const S_OK = 0
- Private Declare Function CreateStreamOnHGlobal _
- Lib "ole32" (ByVal hGlobal As Long, _
- ByVal fDeleteOnRelease As CBoolean, _
- ppstm As Any) As Long
- Private Declare Function OleLoadPicture _
- Lib "olepro32" (pStream As Any, _
- ByVal lSize As Long, _
- ByVal fRunmode As CBoolean, _
- riid As GUID, _
- ppvObj As Any) As Long
- Public Type GUID
- dwData1 As Long
- wData2 As Integer
- wData3 As Integer
- abData4(7) As Byte
- End Type
- Private Declare Function CLSIDFromString _
- Lib "ole32" (ByVal lpsz As Any, _
- pclsid As GUID) As Long
- Private Const sIID_IPicture = "{7BF80980-BF32-101A-8BBB-00AA00300CAB}"
- Private Const GMEM_MOVEABLE = &H2
- Private Declare Function GlobalAlloc _
- Lib "kernel32" (ByVal uFlags As Long, _
- ByVal dwBytes As Long) As Long
- Private Declare Function GlobalLock Lib "kernel32" (ByVal hMem As Long) As Long
- Private Declare Function GlobalUnlock Lib "kernel32" (ByVal hMem As Long) As Long
- Private Declare Function GlobalFree Lib "kernel32" (ByVal hMem As Long) As Long
- Private Declare Sub MoveMemory _
- Lib "kernel32" _
- Alias "RtlMoveMemory" (pDest As Any, _
- pSource As Any, _
- ByVal dwLength As Long)
- Public Function PictureFromBits(abPic() As Byte) As IPicture
- Dim nLow As Long
- Dim cbMem As Long
- Dim hMem As Long
- Dim lpMem As Long
- Dim IID_IPicture As GUID
- Dim istm As stdole.IUnknown
- Dim ipic As IPicture
- On Error GoTo Out
- nLow = LBound(abPic)
- On Error GoTo 0
- cbMem = (UBound(abPic) – nLow) + 1
- hMem = GlobalAlloc(GMEM_MOVEABLE, cbMem) ‘分配可移动的内存
- If hMem Then
- lpMem = GlobalLock(hMem)
- If lpMem Then
- MoveMemory ByVal lpMem, abPic(nLow), cbMem
- Call GlobalUnlock(hMem)
- If (CreateStreamOnHGlobal(hMem, CTrue, istm) = S_OK) Then
- If (CLSIDFromString(StrPtr(sIID_IPicture), IID_IPicture) = S_OK) Then
- Call OleLoadPicture(ByVal ObjPtr(istm), cbMem, CFalse, IID_IPicture, PictureFromBits)
- End If
- End If
- End If
- End If
- Out:
- End Function
- Public Function UTF8_Decode(bUTF8() As Byte) As String ‘二进制解析为UTF8
- Dim lRet As Long
- Dim lLen As Long
- Dim lBufferSize As Long
- Dim sBuffer As String
- Dim bBuffer() As Byte
- lLen = UBound(bUTF8) + 1
- If lLen = 0 Then Exit Function
- lBufferSize = lLen * 2
- sBuffer = String$(lBufferSize, Chr(0))
- lRet = MultiByteToWideChar(65001, 0, VarPtr(bUTF8(0)), lLen, StrPtr(sBuffer), lBufferSize)
- If lRet <> 0 Then
- sBuffer = Mid(sBuffer, 1, lRet)
- End If
- UTF8_Decode = sBuffer
- End Function
以上代码是原作者用vb写的,因为张筱祥用的是C#,所以基本是学习其思路,看完上文可以说g_tk 的算法就很简单了,就是从登陆后的cookie中得到skey 然后执行一段js,加密得到g_tk的值。下面简单说下在C#中的实现过程。
1. 首先 引用COM组件 mscrosoft script control 1.0
2. 直接上张筱祥写好的方法。
string Getgtk(string skey)
{
MSScriptControl.ScriptControlClass sc = new MSScriptControl.ScriptControlClass();
sc.Language = "javascript";
string[] js = new string[7];
js[0] = "function getGTK(str){\r\n";
js[1] = "var hash = 5381;\r\n";
js[2] = "for(var i = 0, len = str.length; i < len; ++i){ \r\n";
js[3] = " hash += (hash << 5) + str.charAt(i).charCodeAt();\r\n";
js[4] = "}\r\n";
js[5] = " return hash & 0x7fffffff;\r\n";
js[6] = "}\r\n";
sc.AddCode(js[0] + js[1] + js[2] + js[3] + js[4] + js[5] + js[6]);
object[] parm = new object[] { skey };
string g_tk = sc.Run("getGTK", ref parm).ToString();
return g_tk;
}
以上计算g_tk 的方法在2011年2月25号测试有效。
Popularity: 18% [?]
最新评论