腾讯QQ空间g_tk算法

   在百度搜索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复制代码

 

http://www.codeages.com/forum.php?mod=viewthread&tid=43004&extra=page%3D1

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值