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
$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);
0111 1111 1111 1111 1111 1111 1111 1111(31 個 1)>>> 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);
| 語言 | 核心代碼 |
|---|---|
| JavaScript | for(i=0,h=5381;i<s.length;i++) h+= (h<<5)+s.charCodeAt(i); return h&0x7fffffff; |
| Python | h=5381; [h:= (h*33 + ord(c)) & 0x7fffffff for c in s]; return h |
| Java | long h=5381; for(char c:s.toCharArray()) h=(h*33+c)&0x7fffffff; return (int)h; |
| Go | h:=5381; for _,c:=range s { h=(h*33+int(c))&0x7fffffff } return int32(h) |