首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 實作 QQ 空間 g_tk 加密算法完整解析

     編輯:PHP 綜合知識

背景知識

g_tk 是騰訊多個 Web 服務(QQ 空間、QQ 郵箱、騰訊文檔等)用於防 CSRF 的 Token 計算算法。其核心是基於 skey 或 p_skey Cookie 值,通過類 DJB Hash 算法生成 32 位整數。

標準算法實作

<?php
/**
 * 計算 QQ 空間 g_tk 值
 * @param string $skey 從 Cookie 獲取的 skey 或 p_skey
 * @return int 32 位正整數 g_tk
 */
function getGTK($skey) {
    $hash = 5381;  // DJB Hash 魔數種子
    
    for ($i = 0; $i < strlen($skey); $i++) {
        // 核心公式:hash = hash * 33 + char_code
        // 位運算優化:(hash << 5) + hash === hash * 33
        $hash += ($hash << 5) + utf8CharCode($skey[$i]);
    }
    
    // 取低 31 位(0x7fffffff = 2147483647)
    return $hash & 0x7fffffff;
}

/**
 * 獲取 UTF-8 字元的 Unicode 碼位
 * 處理多位元組字元(中文、Emoji 等)
 * @param string $char 單字元(可能多位元組)
 * @return int Unicode 碼位
 */
function utf8CharCode($char) {
    $len = strlen($char);
    switch ($len) {
        case 1:  // ASCII (0-127)
            return ord($char);
        case 2:  // 2 位元組字元 (U+0080 - U+07FF)
            return ((ord($char[0]) & 0x1f) << 6) 
                 | (ord($char[1]) & 0x3f);
        case 3:  // 3 位元組字元 (U+0800 - U+FFFF) 含中文
            return ((ord($char[0]) & 0x0f) << 12) 
                 | ((ord($char[1]) & 0x3f) << 6) 
                 | (ord($char[2]) & 0x3f);
        case 4:  // 4 位元組字元 (U+10000 - U+10FFFF) 含 Emoji
            return ((ord($char[0]) & 0x07) << 18) 
                 | ((ord($char[1]) & 0x3f) << 12) 
                 | ((ord($char[2]) & 0x3f) << 6) 
                 | (ord($char[3]) & 0x3f);
        default:
            return 0;
    }
}

算法原理解析

DJB Hash 變體

// 標準 DJB Hash
$hash = 5381;
foreach (str_split($str) as $c) {
    $hash = (($hash << 5) + $hash) + ord($c);  // hash * 33 + c
}

// QQ 空間變體:支援 UTF-8 多位元組字元
$hash += ($hash << 5) + utf8CharCode($char);

關鍵常數 0x7fffffff

  • 二進制:0111 1111 1111 1111 1111 1111 1111 1111(31 個 1)
  • 作用:確保結果為 32 位有號整數正數範圍(0 ~ 2,147,483,647)
  • JavaScript 中 >>> 0 同理

實戰應用場景

// 1. 從 Cookie 獲取 skey/p_skey
function getSkeyFromCookie() {
    // 假設已獲取完整 Cookie 字串
    $cookie = '...; skey=@AbC123; p_skey=xYz789; ...';
    
    // 優先使用 p_skey(QQ 空間專用),其次 skey
    if (preg_match('/p_skey=([^;]+)/', $cookie, $m)) return $m[1];
    if (preg_match('/skey=([^;]+)/', $cookie, $m)) return $m[1];
    return '';
}

// 2. 組裝帶 g_tk 的 API 請求
$skey = getSkeyFromCookie();
$gtk = getGTK($skey);

$apiUrl = "https://user.qzone.qq.com/proxy/domain/taotao.qzone.qq.com/cgi-bin/emotion_cgi_publish_v6?g_tk={$gtk}";

// 3. 發送請求(需攜帶完整 Cookie)
$ch = curl_init($apiUrl);
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query([
        'syn_tweet_verson' => 1,
        'paramstr' => 1,
        'pic_template' => '',
        'richtype' => 1,
        'con' => '測試發說說',
        'feedversion' => 1,
        'ver' => 1,
        'ugc_right' => 1,
        'to_tweet' => 0,
        'to_sign' => 0,
        'hostuin' => $uin,  // 目標 QQ 號
        'code_version' => 1,
        'format' => 'json',
        'qzreferrer' => 'https://user.qzone.qq.com/'
    ]),
    CURLOPT_COOKIE => $cookie,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
]);
$response = curl_exec($ch);

多語言對照實作

語言核心代碼
JavaScriptfor(i=0,h=5381;i<s.length;i++) h+= (h<<5)+s.charCodeAt(i); return h&0x7fffffff;
Pythonh=5381; [h:= (h*33 + ord(c)) & 0x7fffffff for c in s]; return h
Javalong h=5381; for(char c:s.toCharArray()) h=(h*33+c)&0x7fffffff; return (int)h;
Goh:=5381; for _,c:=range s { h=(h*33+int(c))&0x7fffffff } return int32(h)

常見問題

  • Cookie 獲取困難:需模擬登錄或使用瀏覽器擴充套件導出
  • skey 失效:通常 24 小時有效,需定期刷新
  • 編碼問題:確保 $skey 為原始 URL 解碼後的字串,勿雙重解碼
  • 算法變更:騰訊偶爾調整算法,生產環境需加入版本檢測機制
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved