Toutiao ビデオおよびスイカビデオ署名アルゴリズム

青い文字 [ プロトコル解析と復元 ] を クリックし てフォローしてください

共有後に Toutiao で小さなビデオと Xigua ビデオを開くためにブラウザで使用される署名アルゴリズムを分析します。

WeChat wxidを使って友達を追加するという記事を書きましたが、誤ってWeChatの検索ブラックホールに遭遇してしまい、このアカウント史上最大の検索量、閲覧量、注目度を誇る記事となりました。純粋な乾物 報道範囲が限られている場合でも、注目度の高い情報はより大きな利益をもたらす可能性があります。

皆さんのニーズにもっと応えるために、「wxid」を送信して、wxid を使用して WeChat に友達を追加する具体的な方法のスクリーンショットを取得できます。結局のところ、すべてのリクエストに返信することはできませんし、教えることもできません問題は自力で解決する必要があります。

以下はこの記事です。必要な友達はクリックして学ぶことができます。

今日の記事は Toutiao の署名アルゴリズムを分析します。非常に辛口です。その知識は Toutiao に関連する技術的応用に広く使用されています。楽しく学んでいただければ幸いです。

今日の Toutiao は人気のフライドチキンで、多くの人がそれを見つめています。もちろん、Toutiao のさまざまな種類のデータをクロールすることは不可欠です。インターネット上には Toutiao の署名アルゴリズムを分析する記事がたくさんありますが、それらは楽しいものではありません。いくつかの問題があります。ここで、署名署名についてさらに有益な説明を行う。

また、js はこの世で最もゴミな言語と言わざるを得ず、一刻も早く歴史の舞台から退きたいと思っています。

01

署名とは何ですか?

Toutiao では、サーバーはいくつかのキー ノードのデータを検証します。検証の一部として、クライアント上のデータに署名し、サーバー上のデータの有効性を確認します。正当なリクエストです。

たとえば、Toutiao が共有するビデオにアクセスする過程で、ブラウザは自動的に次の URL をリクエストします。

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

この場合、_signature は前後のメッセージには存在せず、Toutiao サーバーから送信される js によっていくつかのパラメーター、つまり署名を基に計算されます。

この _signature が間違っていると、サーバーがそれを見つけて、本当に必要なメッセージを返しません。

上記の URL はビデオの重要な情報をリクエストするために使用されます。_signature が間違っている場合は、Toutiao 独自のデフォルトのビデオが返されます。

したがって、署名はToutiaoが発行したjsアルゴリズムに従ってクライアントが生成した検証値、つまり署名であり、サーバーはそれを検証し、検証状況に応じて異なるフィードバックを返します。

02

署名アルゴリズム

Toutiao の署名で何が起こっているかを知れば、誰もが間違いなく、署名値はクライアント側の js によって生成され、js は簡単に取得でき、js コードは完全に暗号化できないため、署名値は暗号化されないと考えるでしょう。それを理解するのは簡単ではありませんか?

私も最初はそう思っていましたが、署名を生成する js アルゴリズムを見てがっかりしました。難読化後は、いたるところで文字化けが発生し、フォーマットがなく、説明のつかない文字列が大量に表示されます。

それでは少しずつ分析していきましょう。

まず、この署名アルゴリズムはかなり前に逆転されており、インターネットで「Toutiao 署名」を検索するとたくさん見つかりますが、確認したところ、このアルゴリズムは Toutiao ビデオと Xigua ビデオで使用されています。

ここでは、Toutiao と Xigua のビデオをより有効に活用するための分析プロセスを紹介します。

fiddler を使用してパケットをキャプチャすると、Toutiao のさまざまな 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

インターネット上で Web サイトを見つけてフォーマットすると、インターネット上で公開されているクラックされた Toutiao 署名コードの get_as_cp_signature() の一部と基本的に一致していることがわかります。

get_as_cp_signature 関数では、TAC.sign の部分だけが必要なので、as と cp の 2 つの値の生成を直接削除してコードを簡素化できます。

asとcpの2つの値を削除した後の部分が署名の生成に使用される暗号アルゴリズムであり、暗号アルゴリズムを生成するための素材は以下の内容となります。

("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");

ただし、この方法で生成された署名値がリクエストを構築する際に効果的に使用できないのは残念です。この記事にアクセスした友人はおそらくこの問題を抱えているでしょう。前者は乾物ですが、新しいものは何もありません。後者は、乾燥品ですが、新しいものは何もありません。はい、リクエスト作成時に署名値が無効であるという問題を解決できます。

署名アルゴリズム TAC.sign は多くの変数に関連しているため、署名は無効です。

03

署名アルゴリズム分析

署名アルゴリズム全体は、さまざまなデータの sdbmhash ハッシュを計算するプロセスです。ハッシュの内容が異なれば、当然結果も異なります。

まず、署名値が入力パラメータに関連していることは間違いなく、Toutiao Video と Watermelon Video へのアクセスリクエストでは、この入力パラメータは URL の i 値、つまりビデオ ID 値です。

次に、Toutiao Video と Xigua Video へのアクセス リクエストでは、署名は 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>

もちろん文字化けもしますので、気にせずそのまま使ってください。

繰り返しますが、署名はブラウザの 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;

最後に、署名署名は文字化けの第 2 段落の文字化けによって作成された画像値にも関係しており、デバッグの穴によると、画像は PC と携帯電話で異なる可能性があります。 、文字化けが発生します 画像はこんな感じです。

640?wx_fmt=jpeg

このものの sdbmhash ハッシュは、携帯電話で生成されたメッセージの署名値のハッシュと等しくありません。デバッグに使用した携帯電話のハッシュが 723236945 であることを確認するには、総当たりの方法を使用する必要がありました。一方、PC 上のアルゴリズムでこの画像によって生成されたハッシュは、3311753357 です。

これら 4 つの内容を制御した後、正しい署名値を生成することができ、最終的に、デバッグ プロセスに従って 26 バイトの署名値を 5 つの部分に分割できます。

j5qI5 xAY0r xK.9Bq 6f.DU 4-aiP	
5a   |5b   |6c    |5d   |5e	
6c与构造的乱码字图像有关	
5d与浏览器UA有关	
tac与所有的内容有关	
视频id值与5d有关 

04

福祉

前述したように、利用可能な署名値を生成するには 4 つの値を制御する必要がありますが、このうちビデオ ID は入力パラメータであり、tac 値はコード内で直接割り当てることができ、どちらも制御は簡単ですが、確かにUA値はjs由来ですが、現在の動作環境で自動取得される文字化け画像も一部のjsシステム関数によって生成されており、生成プロセスを制御できないため、簡単に置き換えることはできません。

置換コードは次のとおりです。最初は、get_as_cp_signature のケース 77 の UA の置換です。

  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;

2 つ目は、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;

これはすべて、苦労して段階的に作成したコードです。

皆さんの幸せな仕事を祈っています。

忘れずに私に注意を払ってください、あなたは私にやる気を与えます、そして私はあなたが望む乾物をあげます。

さらに、重要なことは、記事が価値がある場合は、右下の「見る」をクリックして友達と共有することです↘


 

おすすめ

転載: blog.csdn.net/yeyiqun/article/details/99311257