Googleの難読化されたJavaScriptと戦うための戦略

Google ReaderのスペースキースクロールをLDR風にするbookmarklet

javascript:(function(){$.prototype.qh=function(a){if(this.ka&&!this.Ee||!this.Ka)return;var b=this.Ee?u(PD):this.ea();b.scrollTop+=a?+100:-100};$.prototype.down=function(a){this.qh(!a)};$.prototype.up=function(a){this.qh(a)}})()

ubuntu+Firefoxでもwin2k+IE6でもできたよ!

でもきっとGoogle側に更新あったらすぐ使えなくなっちゃうからみんな作り方覚えてね!笑

経緯

Google Readerを使ってみたらかなり使いやすくてビックリした。

今まではレート分けして読める機能が好きでLDR使ってたんだけど、Google Readerのタグ分けでも

  • *1
  • *2
  • *3

とかタグを作って振ってやればレート感覚で読める。

ただしレートとして特化しているわけじゃないので分類作業はLDRより多少面倒。

でも僕はフィードを1000も2000も読んでないのでそんなに困ってないです。


また、Google Readerではスペースキーをポンポン押すだけで読み進めることができます。

フィードorタグ内の記事を全部読んでスペースを押すと次のフィードorタグに飛んでくれます。(*)

ただスクロールの量がページ単位なので、これはLDRのように少しずつスクロールして欲しい!


単純にスクロールの量を調節するだけなら

  1. DOM Inspector使ってスクロールバー持ってる要素を探し、
  2. addEventListenerでkeydownイベントとかにスクロール処理を追加して、
  3. ブラウザのPageDownが効かないようにそこでイベントを止める。

とかでよさそうだけども、それだと(*)の機能まで止めてしまう!


そんなこんなでGoogleの難読化されたJavaScriptに挑んでみることにしました。

コードを眺めてみる

var aa="0";function ba(a,b){a=String(a);return(new Array(Math.max(0,b-a.length+1))).join(aa)+a}var j="";function ca(){return Array.prototype.join.call(arguments,j)}var da="/",l=" ",ea=":";function fa(a){var b=a.getFullYear(),c=a.getMonth(),d=a.getDate(),e=a.getHours(),f=a.getMinutes();if(b<=0)b=-b+1;return ca(ba(b,4),da,ba(c+1,2),da,ba(d,2),l,ba(e,1),ea,ba(f,2))}function ga(a){var b=a.getHours(),c=a.getMinutes();return ca(ba(b,1),ea,ba(c,2))}function ha(a){var b=a.getFullYear(),c=a.getMonth(),d=a.getDate();2if(b<=0)b=-b+1;return ca(ba(b,4),da,ba(c+1,2),da,ba(d,2))};//.....以下略

見た瞬間ちょっと諦めたくなりますよね。変数名がaa,ba,ca,...とか orz

最初からこんな変数やら関数やらを追っていくのはさすがにキツいので、何かしらの手がかりが必要です。

戦略1

難読化することのできない文字列をヒントに追いかける。

例えば以下のようなキーワードで検索していきます。

  • DOM要素のidやclassName
  • イベント名
    • "keydown", "click", "mouseover", ...
  • keyCode
    • 13(Enter), 27(Esc), 32(Space), 37,38,39,40(カーソルキー),...
  • "XMLHttpRequest"
  • "setInterval"


まぁJavaScriptが実行されるタイミングは

  • ロード時
  • DOM Eventによる発火
  • setTimeoutなどのタイマーによる発火
  • XMLHttpRequestのresponseが帰ってきたときとか


ぐらいしかないので当然といえば当然ですが、この辺を狙っていきます。

戦略2

今回の場合は『スペースキー』と標的がはっきりしてたので簡単かと思いきやなかなかうまく行きません。

もうちょっと情報が欲しい・・・!!ってことでfirebugのprofileを利用してみる。


以下の手順

  1. profile()
  2. Google Readerの一覧でスペースキーを20回押してみる
  3. ついでに意味のないキーを10回押してみる
  4. profileEnd()
  5. calls(呼び出し回数)をクリックしてソートさせてみる


そしてprofile結果を一部抜粋

func	calls	par	owntime	time	avg	min	max	file
DM	30	2.1%	3.426ms	38.42ms	1.281ms	0.247ms	2.09ms	2574947301-ja-scr... (line 489)
hi	30	0.11%	0.177ms	0.177ms	0.006ms	0.005ms	0.007ms	2574947301-ja-scr... (line 62)
FM	30	1.52%	2.482ms	2.664ms	0.089ms	0.086ms	0.097ms	2574947301-ja-scr... (line 491)
rb	30	0.09%	0.149ms	0.149ms	0.005ms	0.004ms	0.008ms	2574947301-ja-scr... (line 52)
xy	30	0.08%	0.135ms	0.135ms	0.005ms	0.003ms	0.005ms	2574947301-ja-scr... (line 311)
GM	30	0.08%	0.125ms	0.125ms	0.004ms	0.003ms	0.005ms	2574947301-ja-scr... (line 491)
s	30	0.47%	0.77ms	0.77ms	0.026ms	0.014ms	0.029ms	2574947301-ja-scr... (line 77)
kk	20	0.19%	0.308ms	11.76ms	0.588ms	0.328ms	0.792ms	2574947301-ja-scr... (line 455)
kk	20	3.15%	5.13ms	11.45ms	0.573ms	0.32ms	0.774ms	2574947301-ja-scr... (line 455)
oh	20	6.28%	10.22ms	11.09ms	0.555ms	0.334ms	0.623ms	2574947301-ja-scr... (line 453)
ln	20	0.64%	1.048ms	8.017ms	0.401ms	0.36ms	0.475ms	2574947301-ja-scr... (line 457)
ln	20	1.75%	2.849ms	6.969ms	0.348ms	0.311ms	0.419ms	2574947301-ja-scr... (line 457)
ws	20	1.1%	1.792ms	6.088ms	0.304ms	0.165ms	0.36ms	2574947301-ja-scr... (line 538)
d	20	1.49%	2.428ms	4.107ms	0.205ms	0.109ms	0.254ms	2574947301-ja-scr... (line 19)
kk	20	1.22%	1.986ms	1.986ms	0.099ms	0.047ms	0.119ms	2574947301-ja-scr... (line 455)
SO	20	0.12%	0.189ms	0.189ms	0.009ms	0.005ms	0.011ms	2574947301-ja-scr... (line 538)
ln	20	0.95%	1.545ms	1.62ms	0.081ms	0.049ms	0.115ms	2574947301-ja-scr... (line 457)
wo	20	0.12%	0.188ms	0.953ms	0.048ms	0.023ms	0.175ms	2574947301-ja-scr... (line 439)
ln	20	0.05%	0.075ms	0.075ms	0.004ms	0.002ms	0.005ms	2574947301-ja-scr... (line 457)
oh	20	2.22%	3.615ms	15.96ms	0.798ms	0.627ms	0.913ms	2574947301-ja-scr... (line 453)
ts	20	3.68%	5.997ms	6.066ms	0.303ms	0.171ms	0.382ms	2574947301-ja-scr... (line 491)
mb	20	0.04%	0.069ms	0.069ms	0.003ms	0.002ms	0.005ms	2574947301-ja-scr... (line 51)
HM	20	1%	1.63ms	18.27ms	0.914ms	0.717ms	1.038ms	2574947301-ja-scr... (line 490)

この辺りの関数が怪しい箇所の候補となります。

firebugすげー!!!


まだ候補が多いのは『keydown, keyup, keypress』がそれぞれ同じだけ実行されるからで、

owntime, timeとか見ればもうちょい絞り込むことができます。


処理時間が多めにかかってるDM,kk,oh,HMあたりが怪しい。。。

戦略3

これだけヒントがあれば楽勝・・・とは言えないですね、やっぱ(^^;

戦略2でもっと細かく調べればまだまだヒントが得られそうですが、

今回は疲れたのでこの辺で切り上げました。


戦略3、最後はやっぱり根性です!(笑)

今回の結末

結局これがスペースキーを押したとき実行される関数でした。

$.prototype.yo=function(a){if(this.Ee){this.qh(a);return}if(this.ka||!this.Ka)return;if(this.sa==-1){if(a){this.down()}return}var b=this.ja().q,c=b.offsetTop,d=b.offsetHeight,e=c+d,f=this.ea(),g=f.scrollTop,h=f.offsetHeight,i=g+h;if(a&&e<=i||!a&&c>=g){if(a){if(this.sa>=this.ta.length-1){Jx.He.$u()}else{this.down()}}else{this.up()}}else{this.qh(a)}};

この関数中の最初と最後にある『this.qh(a)』がポイント。


qh()とその中のわからない変数を順に追いかける。

$.prototype.qh = function(a) {
  if (this.ka && !this.Ee || !this.Ka) return;
  var b = this.Ee ? u(PD) : this.ea(),
    c = b.offsetHeight,
    d = c-50;
  b.scrollTop += (a? +d : -d);
};

/* getElementByIdのラッパー */
function u(a,b){var c=b||document;return c.getElementById(a)}

/* getElementById("entries") */
$.prototype.ea=function(){return u(OD)};
OD="entries";

結局ph()は#entriesを『1ページ分 - 50px』だけスクロールさせる処理。何気に親切な50px。


スペースを押したときのスクロール処理はph()に収まっているようなので、こんな風に再定義してみる。

$.prototype.qh = function(a) {
  if (this.ka && !this.Ee || !this.Ka) return;
  var b = this.Ee ? u(PD) : this.ea(),
    c = b.offsetHeight,
//  d = c-50;
    d = Math.min(100, c-50);
  b.scrollTop += (a? +d : -d);
};

できた!!!!


冒頭のbookmarkletはこれにちょこっと修正を加えたものです。

今回挑戦してみて反省、感想、考察

まず最初にgetElementByIdのラッパー関数をさがすべきだった!!!

よく使われる関数だし、かなりヒントにもなる!


あとfirebugのprofileでの絞り込み戦術はかなり使える!!!

うまい絞り込み方が他にもいろいろ考えられそう!