頭條小視頻和西瓜視頻signature簽名算法


點擊上方藍字[協議分析與還原]關注我們


分析今日頭條內小視頻和西瓜視頻分享后瀏覽器打開所用的signature簽名算法。

上月寫的一篇關於使用微信的wxid加好友的文章,竟然無意間碰到了一個微信搜索黑洞,成就了本號有史以來搜索量、閱讀量、關注度最大的文章,看樣子純粹的干貨受眾面有限,還是關注度高的信息才能帶來更大的收獲。

為更好地滿足大家的需求,可以發送“wxid”獲取微信使用wxid加好友的具體方法截圖,畢竟我沒法一條條請求都回復,也沒法手把手地教,各位解決問題還是以自助為主。

下面是這篇文章,有需要的朋友可以點進去學習:

微信wxid搜索用戶不存在,怎么加好友?

今天這篇文章,對今日頭條的簽名算法進行分析,相當干貨,里面的知識在今日頭條相關的技術應用中使用非常廣泛,希望大家能夠學習愉快。

今日頭條作為一大當紅炸子雞,很多人都盯着,當然,爬取頭條里各種類型的數據必不可少,網上很多分析頭條signature簽名算法的文章,但都不盡興,有些問題沒有明確,導致實踐起來卻有些問題,這里將給出signature簽名更有價值的說明。

另外,不得不說,js是世界上最垃圾的語言,希望早日退出歷史的舞台。

01

signature是什么?

在頭條里,對一些關鍵節點的數據,服務器會進行校驗,校驗的一部分,就是在客戶端對數據進行簽名,在服務器對數據進行合法性檢查,這樣,就能夠過濾掉很大一部分不合法的請求。

例如,在訪問一個頭條分享的視頻過程中,瀏覽器會自動請求下面這個URL:

https://m.365yg.com/i6714101379172027656/info/?_signature=lyZkrxARynO8Pdz-QfDCYJcmZL&i=6714101379172027656

這個里面,_signature在它前后的報文中是沒有的,是頭條服務器下發下來的js根據一些參數計算出來的,也就是簽名。

這個_signature如果錯誤,服務器就會發現,就不會返回我們真正需要的報文。

上面的這個URL,是用來請求視頻的一些關鍵信息的,如果_signature錯誤,則會返回一個頭條自己的默認視頻。

因此,signature就是一個由客戶端根據頭條下發的js算法生成的校驗值,即簽名,服務器將會對它進行校驗,並根據校驗情況作出不同的反饋。

02

簽名算法

知道了頭條的signature簽名是怎么回事,大家一定會想到,既然signature值是在客戶端由js生成的,而js我們能夠輕松拿到,並且js代碼是沒法真正加密的,那這個簽名值不就很容易算出來嗎?

我一開始也是這么想的,當看到了生成signature簽名的js算法的時候,我氣餒了,這到底是什么玩意?!經過了混淆,里面到處是亂碼,沒有格式,一堆莫名其妙的字符串。

那就正兒八經地一點點的分析吧。

先說下,這個簽名算法很早前就有人將它逆向出來了,網上搜“頭條signature”就能找到一大堆,經過確認,在頭條小視頻和西瓜視頻中使用的就是這個算法。

這里介紹下符合頭條小視頻和西瓜視頻的分析過程,以便更好的使用。

使用fiddler抓包,很容易就能抓到頭條的各類js文件,其中,簽名算法在xigua_video.38p5Q8hF.js這個文件中,下面是對文件格式化后的簽名部分:

640?wx_fmt=png

看着很崩潰吧,里面一堆亂碼,這是js所謂的特性,除了制造不必要的阻礙,沒有任何作用,我們要有信心,只要有能運行js的環境,js必然是裸奔,最終都將還原成瀏覽器可以識別的代碼,事實也確實是這樣。

一步步來把它解開,先將前面那部分來嘗試解開,下面是代碼,直接就輸出結果了:

var x =function(l){return'e(e,a,r){(b[e]||(b[e]=t("x,y","x "+e+" y")(r,a)}a(e,a,r){(k[r]||(k[r]=t("x,y","new x[y]("+Array(r+1).join(",x[y]")(1)+")")(e,a)}r(e,a,r){n,t,s={},b=s.d=r?r.d+1:0;for(s["$"+b]=s,t=0;t<b;t)s[n="$"+t]=r[n];for(t=0,b=s=a;t<b;t)s[t]=a[t];c(e,0,s)}c(t,b,k){u(e){v[x]=e}f{g=,ting(bg)}l{try{y=c(t,b,k)}catch(e){h=e,y=l}}for(h,y,d,g,v=[],x=0;;)switch(g=){case 1:u(!)4:f5:u((e){a=0,r=e;{c=a<r;c&&u(e[a]),c}}(6:y=,u((y8:if(g=,lg,g=,y===c)b+=g;else if(y!==l)y9:c10:u(s(11:y=,u(+y)12:for(y=f,d=[],g=0;g<y;g)d[g]=y.charCodeAt(g)^g+y;u(String.fromCharCode.apply(null,d13:y=,h=delete [y]14:59:u((g=)?(y=x,v.slice(x-=g,y:[])61:u([])62:g=,k[0]=65599*k[0]+k[1].charCodeAt(g)>>>065:h=,y=,[y]=h66:u(e(t[b],,67:y=,d=,u((g=).x===c?r(g.y,y,k):g.apply(d,y68:u(e((g=t[b])<"<"?(b--,f):g+g,,70:u(!1)71:n72:+f73:u(parseInt(f,3675:if(){bcase 74:g=<<16>>16g76:u(k[])77:y=,u([y])78:g=,u(a(v,x-=g+1,g79:g=,u(k["$"+g])81:h=,[f]=h82:u([f])83:h=,k[]=h84:!085:void 086:u(v[x-1])88:h=,y=,h,y89:u({e{r(e.y,arguments,k)}e.y=f,e.x=c,e})90:null91:h93:h=0:;default:u((g<<16>>16)-16)}}n=this,t=n.Function,s=Object.keys||(e){a={},r=0;for(c in e)a[r]=c;a=r,a},b={},k={};r'.replace(/[-]/g,function(e){return l[15&e.charCodeAt(0)]})}("v[x++]=v[--x]t.charCodeAt(b++)-32function return ))++.substrvar .length(),b+=;break;case ;break}".split(""));	
console.log(x);

結果如下:

function e(e,a,r){return (b[e]||(b[e]=t("x,y","return x "+e+" y")))(r,a)}function a(e,a,r){return (k[r]||(k[r]=t("x,y","return new x[y]("+Array(r+1).join(",x[++y]").substr(1)+")")))(e,a)}function r(e,a,r){var n,t,s={},b=s.d=r?r.d+1:0;for(s["$"+b]=s,t=0;t<b;t++)s[n="$"+t]=r[n];for(t=0,b=s.length=a.length;t<b;t++)s[t]=a[t];return c(e,0,s)}function c(t,b,k){function u(e){v[x++]=e}function f(){return g=t.charCodeAt(b++)-32,t.substring(b,b+=g)}function l(){try{y=c(t,b,k)}catch(e){h=e,y=l}}for(var h,y,d,g,v=[],x=0;;)switch(g=t.charCodeAt(b++)-32){case 1:u(!v[--x]);break;case 4:v[x++]=f();break;case 5:u(function (e){var a=0,r=e.length;return function (){var c=a<r;return c&&u(e[a++]),c}}(v[--x]));break;case 6:y=v[--x],u(v[--x](y));break;case 8:if(g=t.charCodeAt(b++)-32,l(),b+=g,g=t.charCodeAt(b++)-32,y===c)b+=g;else if(y!==l)return y;break;case 9:v[x++]=c;break;case 10:u(s(v[--x]));break;case 11:y=v[--x],u(v[--x]+y);break;case 12:for(y=f(),d=[],g=0;g<y.length;g++)d[g]=y.charCodeAt(g)^g+y.length;u(String.fromCharCode.apply(null,d));break;case 13:y=v[--x],h=delete v[--x][y];break;case 14:v[x++]=t.charCodeAt(b++)-32;break;case 59:u((g=t.charCodeAt(b++)-32)?(y=x,v.slice(x-=g,y)):[]);break;case 61:u(v[--x][t.charCodeAt(b++)-32]);break;case 62:g=v[--x],k[0]=65599*k[0]+k[1].charCodeAt(g)>>>0;break;case 65:h=v[--x],y=v[--x],v[--x][y]=h;break;case 66:u(e(t[b++],v[--x],v[--x]));break;case 67:y=v[--x],d=v[--x],u((g=v[--x]).x===c?r(g.y,y,k):g.apply(d,y));break;case 68:u(e((g=t[b++])<"<"?(b--,f()):g+g,v[--x],v[--x]));break;case 70:u(!1);break;case 71:v[x++]=n;break;case 72:v[x++]=+f();break;case 73:u(parseInt(f(),36));break;case 75:if(v[--x]){b++;break}case 74:g=t.charCodeAt(b++)-32<<16>>16,b+=g;break;case 76:u(k[t.charCodeAt(b++)-32]);break;case 77:y=v[--x],u(v[--x][y]);break;case 78:g=t.charCodeAt(b++)-32,u(a(v,x-=g+1,g));break;case 79:g=t.charCodeAt(b++)-32,u(k["$"+g]);break;case 81:h=v[--x],v[--x][f()]=h;break;case 82:u(v[--x][f()]);break;case 83:h=v[--x],k[t.charCodeAt(b++)-32]=h;break;case 84:v[x++]=!0;break;case 85:v[x++]=void 0;break;case 86:u(v[x-1]);break;case 88:h=v[--x],y=v[--x],v[x++]=h,v[x++]=y;break;case 89:u(function (){function e(){return r(e.y,arguments,k)}return e.y=f(),e.x=c,e}());break;case 90:v[x++]=null;break;case 91:v[x++]=h;break;case 93:h=v[--x];break;case 0:return v[--x];default:u((g<<16>>16)-16)}}var n=this,t=n.Function,s=Object.keys||function (e){var a={},r=0;for(var c in e)a[r++]=c;return a.length=r,a},b={},k={};return r

到網上找個網站格式化下,會發現和網上公開的被破解出來的頭條signature代碼的get_as_cp_signature()其中的一部分基本一致。

get_as_cp_signature這個函數中,我們只需要TAC.sign這一部分,所以as,cp這兩個值的生成相關的可以直接刪掉,簡化代碼。

刪除生產as,cp這兩個值的之后的部分就是用來生成簽名的加密算法,而生成加密算法的素材就是緊跟在后面的內容了:

("v[x++]=v[--x]t.charCodeAt(b++)-32function return ))++.substrvar .length(),b+=;break;case ;break}".split("")))()('gr$Daten 袠b/s!l y蛼y墓g,(lfi~ah`{mv,-n|jqewVxp{rvmmx,&effkx[!cs"l".Pq%widthl"@q&heightl"vr*getContextx$"2d[!cs#l#,*;?|u.|uc{uq$fontl#vr(fillTextx$$榫樴笐喔犼步2<[#c}l#2q*shadowBlurl#1q-shadowOffsetXl#$$limeq+shadowColorl#vr#arcx88802[%c}l#vr&strokex[ c}l"v,)}eOmyoZB]mx[ cs!0s$l$Pb<k7l l!r&lengthb%^l$1+s$jl  s#i$1ek1s$gr#tack4)zgr#tac$! +0o![#cj?o ]!l$b%s"o ]!l"l$b*b^0d#>>>s!0s%yA0s"l"l!r&lengthb<k+l"^l"1+s"jl  s&l&z0l!$ +["cs\'(0l#i\'1ps9wxb&s() &{s)/s(gr&Stringr,fromCharCodes)0s*yWl ._b&s o!])l l Jb<k$.aj;l .Tb<k$.gj/l .^b<k&i"-4j!+& s+yPo!]+s!l!l Hd>&l!l Bd>&+l!l <d>&+l!l 6d>&+l!l &+ s,y=o!o!]/q"13o!l q"10o!],l 2d>& s.{s-yMo!o!]0q"13o!]*Ld<l 4d#>>>b|s!o!l q"10o!],l!& s/yIo!o!].q"13o!],o!]*Jd<l 6d#>>>b|&o!]+l &+ s0l-l!&l-l!i\'1z141z4b/@d<l"b|&+l-l(l!b^&+l-l&zl\'g,)gk}ejo{cm,)|yn~Lij~em["cl$b%@d<l&zl\'l $ +["cl$b%b|&+l-l%8d<@b|l!b^&+ q$sign ', [Object.defineProperty(r, "__esModule", {	
value: !0})])};

又是亂碼,網上公開的get_as_cp_signature() 算法將它轉成url編碼了:

r(decodeURIComponent("gr%24Daten%20%D0%98b%2Fs!l%20y%CD%92y%C4%B9g%2C(lfi~ah%60%7Bmv%2C-n%7CjqewVxp%7Brvmmx%2C%26eff%7Fkx%5B!cs%22l%22.Pq%25widthl%22%40q%26heightl%22vr*getContextx%24%222d%5B!cs%23l%23%2C*%3B%3F%7Cu.%7Cuc%7Buq%24fontl%23vr(fillTextx%24%24%E9%BE%98%E0%B8%91%E0%B8%A0%EA%B2%BD2%3C%5B%23c%7Dl%232q*shadowBlurl%231q-shadowOffsetXl%23%24%24limeq%2BshadowColorl%23vr%23arcx88802%5B%25c%7Dl%23vr%26strokex%5B%20c%7Dl%22v%2C)%7DeOmyoZB%5Dmx%5B%20cs!0s%24l%24Pb%3Ck7l%20l!r%26lengthb%25%5El%241%2Bs%24j%02l%20%20s%23i%241ek1s%24gr%23tack4)zgr%23tac%24!%20%2B0o!%5B%23cj%3Fo%20%5D!l%24b%25s%22o%20%5D!l%22l%24b*b%5E0d%23%3E%3E%3Es!0s%25yA0s%22l%22l!r%26lengthb%3Ck%2Bl%22%5El%221%2Bs%22j%05l%20%20s%26l%26z0l!%24%20%2B%5B%22cs'(0l%23i'1ps9wxb%26s()%20%26%7Bs)%2Fs(gr%26Stringr%2CfromCharCodes)0s*yWl%20._b%26s%20o!%5D)l%20l%20Jb%3Ck%24.aj%3Bl%20.Tb%3Ck%24.gj%2Fl%20.%5Eb%3Ck%26i%22-4j!%1F%2B%26%20s%2ByPo!%5D%2Bs!l!l%20Hd%3E%26l!l%20Bd%3E%26%2Bl!l%20%3Cd%3E%26%2Bl!l%206d%3E%26%2Bl!l%20%26%2B%20s%2Cy%3Do!o!%5D%2Fq%2213o!l%20q%2210o!%5D%2Cl%202d%3E%26%20s.%7Bs-yMo!o!%5D0q%2213o!%5D*Ld%3Cl%204d%23%3E%3E%3Eb%7Cs!o!l%20q%2210o!%5D%2Cl!%26%20s%2FyIo!o!%5D.q%2213o!%5D%2Co!%5D*Jd%3Cl%206d%23%3E%3E%3Eb%7C%26o!%5D%2Bl%20%26%2B%20s0l-l!%26l-l!i'1z141z4b%2F%40d%3Cl%22b%7C%26%2Bl-l(l!b%5E%26%2Bl-l%26zl'g%2C)gk%7Dejo%7B%7Fcm%2C)%7Cyn~Lij~em%5B%22cl%24b%25%40d%3Cl%26zl'l%20%24%20%2B%5B%22cl%24b%25b%7C%26%2Bl-l%258d%3C%40b%7Cl!b%5E%26%2B%20q%24sign%20"), [TAC = {}]);

簡單點說,簽名算法一直保持不變,名字就是TAC.sign

再繼續找,找到簽名,知道被簽名值即入參是前面出現的url中的i,即視頻id值:

var _signature = (0, TAC.sign)("6714101379172027656");

但是,遺憾的是,這樣生成的簽名signature值,在構造請求時,卻無法有效使用,訪問本文的朋友估計都是出現了這一問題,前面的是干貨,但沒有新意,后面的才是獨家的,可以解決構造請求時,signature值卻無效的問題。

signature無效,是因為簽名算法TAC.sign與很多個變量有關。

03

簽名算法解析

整個簽名算法,就是一個給各種數據算sdbmhash哈希的過程。算哈希的內容不同,當然結果不同。

首先,毫無疑問,signature值與入參有關,在訪問頭條小視頻和西瓜視頻的請求中,這個入參是url中的i值,即視頻id值。

其次,在訪問頭條小視頻和西瓜視頻的請求中,signature簽名與一個tac值有關,在發起前文中那個url請求的頁面中存在:

<script data-from="toutiao">tac='i)69gg22apbs!i$13zns"0,<8~z|\x7f@QGNCJF[\\^D\\KFYSk~^WSZhg,(lfi~ah`{md"inb|1d<,%Dscafgd"in,8[xtm}nLzNEGQMKAdGG^NTY\x1ckgd"inb<b|1d<g,&TboLr{m,(\x02)!jx-2n&vr$testxg,%@tug{mn ,%vrfkbm[!cb|'</script>

當然也是亂碼,不用管它,直接用就行了。

再次,signature簽名與還與瀏覽器userAgent有關,即發起請求的瀏覽器UA,當然,偽造手機請求得用手機上能出現的UA,這個在網上公開的那個破解出的代碼頭部也有出現

navigator1 = {	
    userAgent: "Mozilla/5.0 (Linux; Android 8.8.2; xxxxx Build/g) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Mobile Safari/537.36"	
}, window = this, window.navigator = navigator1;

最后,signature簽名還與第二段亂碼中的一個亂碼字造出的圖像值有關,根據調試趟過的坑,在PC和手機上大概率這個圖像是不一樣的,在PC上,亂碼生成的圖片是這樣子:

640?wx_fmt=jpeg

這個玩意的sdbmhash哈希,和手機上產生的報文里signature值中的哈希是不等的,我只好用蠻辦法,爆破出我用來調試的手機上的哈希是:723236945,而PC上的算法里這個圖像產生的哈希是:3311753357。

控制好這四個內容,就能夠產生正確的signature值了,最終生成的一個26字節的signature值,根據調試過程,可以按下面分成5個部分:

j5qI5 xAY0r xK.9Bq 6f.DU 4-aiP	
5a   |5b   |6c    |5d   |5e	
6c與構造的亂碼字圖像有關	
5d與瀏覽器UA有關	
tac與所有的內容有關	
視頻id值與5d有關 

04

福利

前面提到,要控制四個值,才能生成可用的signature值,其中,視頻id是入參,tac值,可以直接在代碼中賦值,這兩個都好控制,但UA值,確是js從當前運行環境自動獲取的,亂碼字圖像,也是通過一些js系統函數生成的,沒法控制產生過程,不容易替換。

這里提供替換代碼,首先是UA的替換,在get_as_cp_signature的case 77中

  case 77:	
    y = v[--x];	
    p00 = v[--x];	
    if("navigator"==y)	
    {	
        p = navigator1;	
    }	
    else	
    {	
        p = p00[y];	
    }	
    u(p);	
    // y = v[--x],	
    // u(v[--x][y]);	
    break;

其次是亂碼字圖像哈希值的替換,在get_as_cp_signature的case 62中:

case 62:	
   g = v[--x],	
   k[0] = 65599 * k[0] + k[1].charCodeAt(g) >>> 0;	
   if(k[0]==3311753357)	
      k[0]=723236945;	
   break;

這都是一步步調出來的血汗代碼呀。

祝大家工作愉快。

要記得多多關注我,你給我動力,我給你想要的干貨。

另外,我把贊賞打開了,嘻嘻,不習慣的可以忽略,重要的是,如果文章有價值,點擊右下方“在看”,與朋友們一起分享吧↘



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM