Thursday 27 February 2014

面子书 - 上不到 namewee 粉丝专页

这几天闹得沸沸扬扬的事情,就是无法上到某些亲民联的粉丝专页。

这是相关的访谈。说了除了少数是内鬼干的, 大多数的粉丝专页都是自行选择下线。然后从技术角度看, 有的说 subdirectory 使用同一个IP所以无法封锁, 也有一词说 keyword matching, 亦即说只要有相关的字眼在subdiretory 也会被政府屏蔽。






还有人开记者会, 但是那个记者会时完全可以用程序的方式在同一时间另外一台电脑不停的发出更新请求而达成的, 我并没说肯定假,但是真是假纯粹从报道也得不到 100% 确认。




其实我不清楚其它的粉丝专页的真实故事,  但是现在马来西亚的 IP 是进不到 namewee 黄明志的粉丝专页, https://www.facebook.com/pages/Namewee-黃明志/160059088428




MCMC 否认。



现在就让我探讨 namewee 的粉丝专页是自己关的, 还是政府关的。




首先, 先让我确认政府在不需要依赖面子书管理系统的情况下, 有没有能力用 keyword matching 来屏蔽黃明志粉丝专页。

当然, 不需要去想 MCMC 有没有破解 ssl, 因为只需要简单的逻辑就知道不切实际。



简单的逻辑, 利用 Graph API 请求专页的基本信息。

马来西亚 IP。




Pakistan的 IP (请用 HTTPS 测试才会正确)。




政府用 keyword matching 把含有 160059088428 字眼 的subdirectory 屏蔽了专页信息?

先别着急,再看下一个实验。

https://graph.facebook.com/search?q=namewee&type=page&access_token=xxx

马来西亚 IP。没有 160059088428  的信息。




Pakistan的 IP 。160059088428 就在头一位。namewee 专页信息是有办法通过 API response 来获得 。




到这里可以知道, 只有超高技术的未来科技, 人工智能过滤系统 (比中国长城的防火墙还要牛 N*N 倍) , 才有办法在不影响速度的情况下, 把 API 的 json 解析后过滤 namewee 以及其它被禁的目标,再转送回同样数量的 API 回应。

再来一个证据, limit=1



政府并没有动手脚吧唯一一个的response给丢掉然后换个别的那么神。

再来一个例子, https://www.facebook.com/photo.php?fbid=10151545348923429

马来西亚 IP



Pakistan的 IP


成千上万亿的不同的 photo ID, 政府有能力利用 keyword matching 来过滤 URL subdirectory? 别开玩笑了。

Facebook detect IP 的方法而过滤上面所展示的信息, 只有 Facebook 内部才干得到。

那么第二个问题来了, 会不会政府施压, 叫 Facebook 让马来西亚人上不到 namewee 粉丝专页 ?

让我 show 给你看, 什么才是政府施压。

Pakistan 活生生的例子。 http://en.wikipedia.org/wiki/Internet_censorship_in_Pakistan

Pakistan IP 上不到 http://torrentz.eu/



Pakistan IP 上不到 https://twitter.com/



画面虽然不同, 但共同点是回应 403 forbidden。

马来西亚 IP 浏览 https://www.facebook.com/pages/Lets-Draw-Mohammed/124115974283465


Pakistan 政府在 Facebook 管理系统的帮助下,  Pakistan IP 浏览  https://www.facebook.com/pages/Lets-Draw-Mohammed/124115974283465



会回应 302 Ok,  然后转去 https://www.facebook.com/home.php

转去 https://www.facebook.com/home.php,  用马来西亚IP上 namewee 粉丝专页也是哦...

问: 难道 namewee 粉丝专页就是这样屏蔽的?

答案: 通过详细的检查不同的可能性, 无论 API, ajax, 还是 网页找不到页面的 error/http code, 都是一样的 response  behaviour, 也就表示 Facebook 是以同一个方法来对待被屏蔽的。
如果 Facebook handle 屏蔽的系统跟自行设定的系统不一样, 这种 response behaviour 一样的几率几乎是零。
也就是说, Pakistan 在Facebook 的帮助下屏蔽, 是跟自行设定里头屏蔽一样的。唯一可能不一样的,是无法删除那个设定。
由于跟设定一样, 所以是难以从外部的电脑技术角度来区分是否自导自演的。
只有 Facebook 才知道原因. 

可能性:

1. 受政府压力而屏蔽  namewee 粉丝专页 ?

- MCMC 已否认。大马虽然用户多但仅仅是区区小国, 面子书帮助 Pakistan 也是因为闹上法庭才被迫妥协的, 闹大的,而不是悄悄然的妥协。

2. 自导自演 ?

- 我不是当事人肚里的虫, 没意见。至于记者会现场证明的程度,我也不是观众。

3.  举报过多而屏蔽  namewee 粉丝专页 ?

- 如果面子书的系统会这么无聊在举报上细分举报人的国家,然后只屏蔽巴仙率最多的那个国家的话, 那你就姑且相信吧。


Saturday 22 February 2014

Youtube - 如何有效的 stalk 别人的订阅


通常我们要 stalk 别人的订阅频道, 都是去简介, 如图所示, 这家伙订阅了 4 个频道。





可是有些人的订阅是非常多的。比如说这家伙 https://www.youtube.com/user/milmiLxX/about




这时候就有问题了, 因为你会发现顶多只能看 99 或 100 个频道。又没有下一页的按钮之类的东西。




可能你会说, 活动栏目也应该能看完他的所有订阅




但是太麻烦了,因为没有 "订阅频道" 活动类型给你过滤, 所以要从所有活动大海捞针找, 眼睛都痛。





现在就教你非常简单的方法。



在那个人的专页里, 随便右键按一个地方, 选 Inspect Element。 如果没有这个选项, 直接按 F12 键也可以。




浏览器下面会跑出来新的窗口, 拖拉把窗口拉大, 找 Console 栏目。




把下面的代码一字不漏选完, 然后 copy

var index = 1;
var FirstPage = true;
var cid = 1;
var obj = "";
var channelId = "";
var month = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
function subGui(e) {
    function loop(li2, e) {
        var li = li_create("outside");
        var ul = d.createElement("ul");
        ul.style.listStyleType = "none"; //rm bullet
        var div3 = div_create("", "", "", "", "110px", "104px");
        var title = e.yt$username.display || "";
        var description = e.yt$countHint.$t || "";
        if (description) {
            description = description + ' videos'; //oso int to str
        } else {
            description = 'No video';
        }
        var channelIdE = e.yt$channelId.$t || "";
        var link = "https://www.youtube.com/channel/" + channelIdE;
        var jpg = e.media$thumbnail.url || "";
        var pub = e.published.$t || "";
        if (pub) {
            var dT = new Date();
            dT.setTime(Date.parse(pub));
            gname = 'Subscribed on ' + month[dT.getMonth()] + ' ' + dT.getDate() + ', ' + dT.getFullYear() + ' at ' + dT.toTimeString().split(" ")[0];
        } else {
            gname = '';
        }
        var im = img_create("0", "0", "100px", "100px", jpg, jpg);
        var div = div_create("center", "100px", "100px");
        var a = a_create(jpg);
        var div2 = div_create("center", "102px", "102px", "left");

        var a_title = a_create(link, title);
        var span = d.createElement("span");
        span.appendChild(a_title);
        var div4 = div_create("", "", "", "", "", "", "hidden", "normal");
        div4.appendChild(span);

        var gname = d.createTextNode(gname);
        var cite = d.createElement("cite");
        cite.style.color = "#006621";
        cite.style.fontStyle = "normal";
        cite.appendChild(gname);
        var div5 = div_create("", "", "", "", "", "", "", "", "2px");
        div5.appendChild(cite);

        var desc = d.createTextNode(description);
        var span2 = d.createElement("span");
        span2.appendChild(desc);
        var div6 = div_create("", "", "", "", "", "", "", "", "", "509px", "1px 0 4px");
        div6.appendChild(span2);

        div.appendChild(im);
        a.appendChild(div);
        div2.appendChild(a);
        li2.appendChild(div2); //left side pic
        li.appendChild(div4); //title
        li.appendChild(div5); //date
        li.appendChild(div6); //video(s) count
        ul.appendChild(li);

        div3.appendChild(ul);
        li2.appendChild(div3); //right side text area
    }

    function loop2(ol, e) {
        var li2 = li_create("outside");
        loop(li2, e);
        ol.appendChild(li2);
    }
    var ol = d.createElement("ol");
    ol.start = "" + index; //int to str
    loop2(ol, e);
    docBody.appendChild(ol);
    index++;
}

function set_info() {
    docBody.innerHTML = "";
    docHead.innerHTML = '<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head>';
    var profURL = "https://www.youtube.com/channel/UC" + channelId;
    var title = obj.feed.title.$t + ' (~ ' + obj.feed.openSearch$totalResults.$t + ' channels)';
    profImgT = "https://i.ytimg.com/i/" + channelId + "/1.jpg";
    var profImg = img_create("0", "0", "100px", "100px", profImgT, profURL);
    docBody.appendChild(profImg);
    var a_user = a_create(profURL, title);
    var h3 = d.createElement("h3");
    h3.style.display = "block";
    h3.appendChild(a_user);
    docBody.appendChild(h3); //tab title
}

function img_create(m, b, w, h, sc, t) {
    var i = d.createElement('img');
    sc = sc || "";
    t = t || "";
    i.style.margin = m;
    i.style.border = b;
    i.style.width = w;
    i.style.height = h;
    i.src = sc;
    i.title = t;
    return i;
}

function a_create(h, t) {
    var a = d.createElement("a");
    if (h != null) a.href = h;
    a.target = "_blank";
    if (t != null) {
        var link_text = d.createTextNode(t);
        a.appendChild(link_text);
    }
    return a;
}

function div_create(t, w, h, f, ml, mh, ov, ws, pt, mw, m) {
    var dv = d.createElement("div");
    if (t != null) dv.style.textAlign = t;
    if (f != null) dv.style.setProperty('float', f); //resevered word float failed in firefox
    if (w != null) dv.style.width = w;
    if (h != null) dv.style.height = h;
    if (ml != null) dv.style.marginLeft = ml;
    if (mh != null) dv.style.minHeight = mh;
    if (ws != null) dv.style.whiteSpace = ws;
    if (pt != null) dv.style.paddingTop = pt;
    if (mw != null) dv.style.maxWidth = mw;
    if (m != null) dv.style.margin = m;
    return dv;
}

function li_create(lsp) {
    var l = d.createElement("li");
    l.style.listStylePosition = lsp;
    return l;
}

function sub_tab() {
    if (!FirstPage) {
        pid = "&start-index=" + cid;
        var m = document.getElementById("moreButton");
        m.parentNode.removeChild(m); //rm prev btn
    } else {
        pid = "&start-index=1";
    }
 var su = 'https://gdata.youtube.com/feeds/api/users/'+uid+'/subscriptions?v=2&alt=json&max-results=50' + pid;
 console.log("Requesting..." + su);
 var xhr = new XMLHttpRequest();
 xhr.open("GET", su);
 xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
 xhr.send();
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
         var da = xhr.status;
         if (da == 200) {
                d = document;
                docBody = d.getElementsByTagName("body")[0];
                docHead = d.getElementsByTagName("head")[0];
             var r = xhr.responseText;
                obj = JSON.parse(r); //obj.feed.title.$t
                if ("entry" in obj.feed) {
                 if (FirstPage) {
                     FirstPage = false;
                     channelId = obj.feed.author[0].yt$userId.$t;
                     set_info();
                 }
                    var e = obj.feed.entry;
                    for (var i = 0; i < e.length; i++) {
                        subGui(e[i]);
                    }
                    if (obj.feed.openSearch$totalResults.$t > cid) {
                        cid = cid + 50
                  var moreBtn = d.createElement("input");
                  moreBtn.id = "moreButton";
                  moreBtn.type = "button";
                  moreBtn.value = "More 更多";
                  moreBtn.onclick = sub_tab;
                  docBody.appendChild(moreBtn);
                    } else {
                        cid = 0
                    }
                } else if (FirstPage) {
                    channelId = obj.feed.author[0].yt$userId.$t;
                    set_info();
                    FirstPage = false
                    var unav_text = d.createTextNode("Nothing :(");
                    docBody.appendChild(unav_text);
                }
         }
        }
    }
}
try {
    var uid =  window.location.href.split(/\/channel\/|\/user\//)[1].split(/\/|\?|#/)[0];
} catch (err) {}
if (uid) {
    console.log("[0] User id is:" + uid);
    sub_tab()
} else {
   console.log("Failed to recognize user id");
}



paste 在 Console 栏目的箭头 >> 的右边. 如下图所示。




按ENTER键。代码就会开始运行, 然后会看见那个人的订阅页面。

这个方法最变态的地方是, 可以看见那个人在何年何月何日, 精准到几分几秒订阅。




一次最多只能显示 50 个以下 频道。 如果要看更多频道, 可以按 More 更多的按钮。一直按到按钮消失了, 就表示到底了。




开头的题目写了 Subscriptions of Dennis Lim Ming (~ 462 channels) , 表示大约 ~ 462 频道, 但是通常都是少一些的。其实只有 453 个频道。




如果你在简介页面看不到任何频道, 代码就不会有任何反应,比如说 name wee 。https://www.youtube.com/user/namewee/about




原因可以两种, 一是他没有任何订阅,二是他的订阅设置打勾不公开




干杯 :)


Thursday 20 February 2014

电脑小白基础知识 - 如何保护自己免受 ARP 欺骗攻击

ARP 欺骗攻击 (英文叫 ARP spoofing), 只要在同一个局域网, 比如说公司之类的, 都有 ARP 攻击的可能。

这种攻击已经是老掉牙了, 但是仍然有很多电脑小白不懂得如何保护自己。

先让我展示实战的例子(例子中的 IP 和 MAC 地址是虚构的) 。



如何盗取 Youtube Cookies:

1.
假设我的 IP  是 192.168.1.21, MAC 地址是 1e:3a:13:b2:ee:22


假设受害者的 IP  是 192.168.1.50


 假设 Gateway 的 IP  是 192.168.1.7




2.
使用 arpspoof 命令, 不停的跟 Gateway(192.168.1.7) 说, 192.168.1.50(受害者的 IP) 是在(is-at1e:3a:13:b2:ee:22 MAC 地址, 其实根本就是我的 MAC 地址来的。因为想骗 Gateway 把所有寄去 192.168.1.50 的数据包传送给我。


arpspoof 下载安装请用 apt-get /yum install dsniff, 而不是 apt-get /yum install arpspoof 
* -t 是 target 的缩写, 其实我比较喜欢叫它 tell, 比较容易明白。
* 你的网卡名可能不叫 eth0, 这时候就要加多 -i 网卡名。网卡名可以从 ifconfig 获取。
*至于上图的那个 a1:bc:12:d7:c2:a8, 就是 Gateway 192.168.1.7 IP 的 MAC 地址, 可以通过 arp -n 来查询到。




3.
其实我上面所讲的 arpspoof 192.168.1.50 -t 192.168.1.7 可有可无, 不是盗取 Youtube Cookies 的主角, 因为 Server 回应的不是没有Cookies就是加密的,  我们真正要做的 arpspoof 192.168.1.7 -t 192.168.1.50

如下图所示,我不停的跟 192.168.1.50(受害者的 IP)  说, Gateway(192.168.1.7) 是在(is-at) 1e:3a:13:b2:ee:22 MAC 地址, 其实根本就是我的 MAC 地址来的。因为想骗受害者把所有寄去 192.168.1.7 的数据包传送给我。


*12:3a:4a:5a:6b:7c 是受害者的MAC 地址。


4.
这时候有些攻击者忘了做这个 echo 1 > /proc/sys/net/ipv4/ip_forward (原本是0,  把 0 变成 1允许转送 ) 命令, 就会导致受害者上不到网。因为数据包送来你家地址, 你又不转送去原本要去的地址, 受害者当然上不到网。


*如果你是受害者,可以运行 arp -n, 就可以发现有个仆街的 MAC地址 跟 Gateway 的 MAC地址 一模一样, 明显在对你进行攻击, 不过只限于 -t 192.168.1.50。


*如果是 -t 192.168.1.7 的话, 是 Gateway 被骗, 不是你被骗,就要 Login 去 Gateway 那里看了。你可以运行 arpspoof 192.168.1.50 -t 192.168.1.7  跟那仆街拼过, 但似乎无意义
* 当然, 可以同时骗两边,  -t 192.168.1.7 -t 192.168.1.50


5.
我这时候会打开 Wireshark 网络嗅探器, 选左上角的我的网卡 eth0 开始跑后, 会很多 log, 必须输入 tcp.flags.push == 1 在 Filter 的格子里按 ENTER, 来过滤不重要的 log。



6.
受害者进入某个网页,




7.
有个 Youtube 的影片嵌入 (Embed) 在网页里。Youtube 本身有保护机制会强制性把 Youtube 的 link 从 http 重定向去 https。

如图所示,http://www.youtube.com/v/TJkmc8-eyvE?version=3&hl=en_US 301 Moved Permanetly重定向去 Response Headers 的 Location https://www.youtube.com/v/TJkmc8-eyvE?version=3&hl=en_US


http 重定向去 https 保护机制的出发点是好的, 但却避免不了重定向前的第一次请求就已经用未加密的 HTTP 协议送出 Cookie 。



8.
Wireshark 抓到的数据包。复制明文去慢慢研究。



我们只需要拿 SID 以及 HSID 两个 Cookie 就够了。
Cookie: VISITOR_INFO1_LIVE=xxx; demographics=xxx; YSC=xxx; SID=xxx; HSID=xxx; APISID=xxx; LOGIN_INFO=xxx; PREF=al=zh-CN&f1=50000000&fv=11.2.202
*不同的地方/service需要不同的Cookie, 并不意味着只需要 SID 和 HSID 就可以跑天下。

9.
随便试一下curl -vLk -e 'http://www.youtube.com/' -A 'Mozilla/5.0' --cookie 'SID=xxx;HSID=xxx' 'http://www.youtube.com/' > stalkeme


* 加上 Useragent 是良好的习惯, 不相信? 试下curl www.google.comcurl -A 'Mozilla/5.0' www.google.com 就懂我在讲什么了。



10.
打开 stalkeme(opps..写错字了 =.=), 成功进到受害者 Youtube 的页面。




Youtube 鼓励你们用 www.youtube-nocookie.com 来 embed, 就是为了区分有Login Cookie 和没有Login Cookie 的 domains (有点误导性, 并非完全没有Cookie) , 从而防止 Login 的 Cookie 在embed 的时候就传送。 当然, 这个只是减少受害者而已,无法根治。


* 可能你会问, 那 www.youtube-nocookie.com 能看私人影片吗? 当然能, 因为播放影片的 API 是隶属于 youtube.com, 而不是  youtube-nocookie.com。



如何保护自己:

1. 绑定命令: arp -s 192.168.1.7 a1:bc:12:d7:c2:a8 
有 了 Gateway 固定的地址后, 这样你就不会相信攻击者讲它才是 Gateway 的地址。可是这样做是不足够的, 如果你无法在 Gateway 那边绑定你的地址, 仍然允许 Gateway 被 攻击者骗。
* arp -d 192.168.1.7  可以删除。 重启网卡后也会删除, 所以会有一些文章讨论如何在启动网卡的时候运行绑定命令。



2.  利用 HSTS (HTTPS Strict Transport Security) 机制。

 这方法可以防范 sslstrip 进阶攻击 (把 https 转换回 http  的攻击, 名字也非常明确阐述它的攻击方法, ssl strip,  脱掉 ssl)。

 用 Google Chrome 打开 chrome://net-internals 地址, 选左上角的列表, 选 HSTS。




输入你要强迫用 HTTPS 的域名,比如说  youtube.com, 然后打勾 subdomain 也要(意思是说 www.youtube.com 或 m.youtube.com 也都在保护伞下),最后点击 Add 按钮就可以了。




如图所见, 你可以删除已经加上去的域名 (Delete domain), 也可以询问详情  (Query domain).




3. Logout gmail/Youtube 过后才游览 blogspot. 因为 blogspot 不支持 HTTPS . 如果你利用 HSTS 来强迫 blogspot,你就没办法打开 blogspot.。

如果是自己的 account , 比如说 www.blogspot.com, www.blogger.com, 或者其它网站也好, 请养成一种习惯, 输入 https:// 前缀。 比如说 https://www.blogspot.com





干杯 :)



















Saturday 15 February 2014

谷歌 - 如何窥看别人隐藏的 +1


公告 3 Jun 2014: 代码暂时不能使用了, 我懒得去更新 ~
更正 19 Feb 2014: 不适用于中文字当地址名字的目标用户, 我有解决办法,但很懒改... 

我们常常看见网站有 plusone g+1 的按钮:



如果是自己按过 g+1 的话, 你的 Google Plus 帐号图片会出现在旁边, 朋友的话, 也有一定的几率在旁边看见:





 至于那个数目 +3 是推荐在 Google Search 的次数.




分享是另一回事哦, 不影响 g+1 的数目, 不要混淆。你可以分享很多次。




如果是你或你的朋友的话 +1, 会显示在 Google Search 的下面, 不过, 仅限Google Plus 圈子里的朋友。如果是你自己+1,Google Search 会很快更新, 但是朋友的话,更新会花几天不定:




我这篇文章探讨的是 Google Search 的+1, 不是如下图所示的帖子的 +1哦, 不要混淆。




 还有一点,  g+1的按钮不限于别的网站看到的, 因为在 Google Plus 专页也能按 g+1 推荐在 Google Search。 只不过按钮不一样而已。如下图所示, +7047448 按钮。




你的 Google Search +1 过的历史可以在自己专页 的 +1 栏目看见, 当然你也可以看见别人的。




 可是大多数人的隐私设定都是隐藏了 +1  的栏目。




现在我就教你简单的步骤来实现窥看:



步骤 1. 

进入你要窥看的目标专页。比如说如图中的例子,LadyGaga专页。




步骤 2.

(i) 在那个页面随便一个地方用滑鼠按右键, 选 "Inspect Element"(检查元素)。
(ii) 有些浏览器直接按F12键就可以了。




步骤 3.

下面会出现新的窗口(你需要用滑鼠把那个窗口拖拉大一点),你要选 Console。 不同的浏览器(IE, Chrome, Firefox)图形设计都不同, 但都是一样的步骤, 找到 Console 点击就可以了。

Gogole Chrome 浏览器的 Console:




IE 浏览器的Console:



Firefox 浏览器的Console:




步骤 4.

把下面我写的代码全选,然后copy复制。记得要一字不漏copy, 代码很敏感的,  这个代码很长, 你可能需要scroll。


var FirstPage = true;
var AtleastOneItem = false;
var owner = "";
var index = 1;

function set_info() {
    docBody.innerHTML = "";
    docHead.innerHTML = '<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head>';
    var user = "'s +1's";
    if (owner) {
        user = owner + user;
    } else {
        user = userid + user;
    }
    var profURL = "https://plus.google.com/" + userid;
    plusones = "https://plus.google.com/s2/photos/profile/" + userid;
    var profImg = img_create("0", "0", "100px", "100px", plusones, profURL);
    docBody.appendChild(profImg);
    var a_user = a_create(profURL, user);
    var h3 = d.createElement("h3");
    h3.style.display = "block";
    h3.appendChild(a_user);
    docBody.appendChild(h3); //tab title
}

function decodeEntities(s) {
    s = s.replace(/\\u003d/g, "=").replace(/\\u0026/g, "&"); //my extra, /*/g for replace all
    var str, temp = document.createElement('p');
    temp.innerHTML = s;
    str = temp.textContent || temp.innerText;
    temp = null;
    return str;
}

function img_create(m, b, w, h, sc, t) {
    var i = d.createElement('img');
    sc = sc || "";
    t = t || "";
    i.style.margin = m;
    i.style.border = b;
    i.style.width = w;
    i.style.height = h;
    i.src = sc;
    i.title = t;
    return i;
}

function a_create(h, t) {
    var a = d.createElement("a");
    if (h != null) a.href = h;
    a.target = "_blank";
    if (t != null) {
        var link_text = d.createTextNode(t);
        a.appendChild(link_text);
    }
    return a;
}

function div_create(t, w, h, f, ml, mh, ov, ws, pt, mw, m) {
    var dv = d.createElement("div");
    if (t != null) dv.style.textAlign = t;
    if (f != null) dv.style.setProperty('float', f); //resevered word float failed in firefox
    if (w != null) dv.style.width = w;
    if (h != null) dv.style.height = h;
    if (ml != null) dv.style.marginLeft = ml;
    if (mh != null) dv.style.minHeight = mh;
    if (ws != null) dv.style.whiteSpace = ws;
    if (pt != null) dv.style.paddingTop = pt;
    if (mw != null) dv.style.maxWidth = mw;
    if (m != null) dv.style.margin = m;
    return dv;
}

function li_create(lsp) {
    var l = d.createElement("li");
    l.style.listStylePosition = lsp;
    return l;
}

function plusoneGui(title, hname, link, jpg, description) {
    function loop(li2, title, hname, link, jpg, description) {

        var li = li_create("outside");
        var ul = d.createElement("ul");
        ul.style.listStyleType = "none"; //rm bullet
        var div3 = div_create("", "", "", "", "110px", "104px");

        var title = title || "";
        var hname = hname || "";
        var link = link || "";
        var jpg = jpg || "";
        var description = description || "";

        var im = img_create("0", "0", "100px", "100px", jpg, jpg);
        var div = div_create("center", "100px", "100px");
        var a = a_create(jpg);
        var div2 = div_create("center", "102px", "102px", "left");

        var a_title = a_create(link, title);
        var span = d.createElement("span");
        span.appendChild(a_title);
        var div4 = div_create("", "", "", "", "", "", "hidden", "normal");
        div4.appendChild(span);

        var hname = d.createTextNode(hname);
        var cite = d.createElement("cite");
        cite.style.color = "#006621";
        cite.style.fontStyle = "normal";
        cite.appendChild(hname);
        var div5 = div_create("", "", "", "", "", "", "", "", "2px");
        div5.appendChild(cite);

        var desc = d.createTextNode(description);
        var span2 = d.createElement("span");
        span2.appendChild(desc);
        var div6 = div_create("", "", "", "", "", "", "", "", "", "509px", "1px 0 4px");
        div6.appendChild(span2);

        div.appendChild(im);
        a.appendChild(div);
        div2.appendChild(a);
        li2.appendChild(div2); //left side pic
        li.appendChild(div4); //title
        li.appendChild(div5); //host
        li.appendChild(div6); //desc
        ul.appendChild(li);

        div3.appendChild(ul);
        li2.appendChild(div3); //right side text area
    }

    function loop2(ol, title, hname, link, jpg, description) {
        var li2 = li_create("outside");
        loop(li2, title, hname, link, jpg, description);
        ol.appendChild(li2);
    }
    var ol = d.createElement("ol");
    ol.start = "" + index; //int to str
    loop2(ol, title, hname, link, jpg, description);
    docBody.appendChild(ol);
    index++;
}

var cid = "";

function parse_err(e) {
    console.log("[" + e + "] Unknown parsing, please inform author.");
    throw "stop execution";
}

function plusone_tab() {
    if (cid) {
        cid = "&ct=" + cid;
        var m = document.getElementById("moreButton");
        m.parentNode.removeChild(m); //rm prev btn
    } else {
        cid = "";
    }
    var pu = "https://plus.google.com/_/plusone/get?oid=" + userid + cid;
    console.log("Requesting..." + pu);
    var xhr = new XMLHttpRequest();
    xhr.open("GET", pu);
    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xhr.send();
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
            var da = xhr.status;
            if (da == 200) {
                var r = xhr.responseText;
                var plusoneArray = r.split('\n');
                cid = ""; //reset
                d = document;
                docBody = d.getElementsByTagName("body")[0];
                docHead = d.getElementsByTagName("head")[0];
                for (var i = 0; i < plusoneArray.length; i++) {
                    var line = plusoneArray[i];
                    if (line.indexOf('["1/') != -1) {
                        if (line.indexOf('",1,,,"') != -1) { //may empty desc, so it's not always got " surround
                            var quotes = '"';
                        } else { //no quotes
                            var quotes = '';
                        }
                        var lineArray = line.split('",1,,,' + quotes);
                        if (lineArray.length > 1) {
                            var l = lineArray[1];
                            var lineArray2 = l.split(quotes + ',,1');
                            if (lineArray2.length > 1) {
                                var dDesc = decodeEntities(lineArray2[0]);
                                var theRest = lineArray2[1];
                                var lineArray3 = theRest.split('",');
                                if (lineArray3.length > 3) {
                                    //if out of bound, would still show and handle by gui
                                    var dLink = decodeEntities(lineArray3[0].split(',["')[1]);
                                    var dTitle = decodeEntities(lineArray3[1].split('"')[1]);
                                    var dHost = decodeEntities(lineArray3[2].split(',"')[1]);
                                    var dImg = decodeEntities(lineArray3[3].split('fallback_url\\u003d')[1].split('"')[0]);
                                    AtleastOneItem = true;
                                    for (i++; i < plusoneArray.length; i++) {
                                        if (plusoneArray[i].indexOf('["1/') != -1) {
                                            --i;
                                            break;
                                        } else {

                                            if ((/^,[0|1],"\/\//).test(plusoneArray[i])) { // //,0,"//images OR ,1,"//images
                                                var tmpImg = decodeEntities(plusoneArray[i].split('","')[1].split('"')[0]);
                                                if (tmpImg) {
                                                    dImg = tmpImg;
                                                    break;
                                                }
                                            } else if ((/^,"/).test(plusoneArray[i])) { //StartsWith ," 
                                                //possible when next page's last item use default img
                                                cid = plusoneArray[i].split('"')[1];
                                            }
                                        }
                                    }
                                    if (AtleastOneItem) {
                                        AtleastOneItem = false; //reset
                                        if (FirstPage) {
                                            FirstPage = false;
                                            set_info();
                                        }
                                        plusoneGui(dTitle, dHost, dLink, dImg, dDesc);
                                    } else {
                                        console.log("No item found");
                                    }

                                } else {
                                    parse_err("2");
                                }
                            } else {
                                parse_err("1");
                            }

                        } else {
                            parse_err("0");
                        }
                    } else if ((/^,"/).test(plusoneArray[i])) { //StartsWith ,"
                        cid = plusoneArray[i].split('"')[1];
                    }
                }
                if (cid) {
                    console.log("Next cid:" + cid);
                    var moreBtn = d.createElement("input");
                    moreBtn.id = "moreButton";
                    moreBtn.type = "button";
                    moreBtn.value = "More 更多";
                    moreBtn.onclick = plusone_tab;
                    docBody.appendChild(moreBtn);
                } else {
                    console.log("No data");
                    if (FirstPage) {
                        set_info();
                        var unav_text = d.createTextNode("Nothing :(");
                        docBody.appendChild(unav_text); //tab title
                    }
                }
            } else {
                console.log("Failed");
            }
        }
    }
}

function checkid() {
    var ownerArray = document.querySelectorAll('[data-owner]');
    if (ownerArray.length > 0) {
        userid = ownerArray[0].getAttribute("data-owner");
        if (userid) {
            return userid;
        }
    }
}
var userid = checkid()
if (!userid) {
    userid = window.MB_viewerId; //own page only if checkid() failed, so sequence is very important!
    if (userid) {
        console.log("data-owner success");
    } else {
        var pathArray = window.location.pathname.split('/');
        ///communities currently only digit, otherwise is always same with user profile, .e.g https://plus.google.com/+Dell/posts
        if (pathArray.length > 2) {
            if ((pathArray[1] == 'app') && pathArray[2] == 'basic') {
                if (pathArray[1] != "communities") {
                    owner = pathArray[3]; //plus.google.com/app/basic/USERID/posts
                } else {
                    owner = pathArray[4];
                }
            } else if (pathArray[1] == "communities") {
                owner = pathArray[2];
            } else if (/^[a-zA-Z0-9]$/.test(pathArray[1])) {
                if (/^[0-9]$/.test(pathArray[2])) { //plus.google.com/u/1/USERID/posts 
                    owner = pathArray[3];
                } else { //plus.google.com/b/118094714132938124668 //currently no profile pic
                    owner = pathArray[2];
                }
            } else {
                owner = pathArray[1];
            }
            if (!/^\d+$/.test(owner)) {
                if (owner[0] != '+') {
                    console.log("[1] Failed. Unable to recognize userid.");
                    throw "stop execution";
                } else {
                    m_url = "https://plus.google.com/app/basic/" + owner + "/"
                    console.log("Tryng..." + m_url);
                    var xhr = new XMLHttpRequest();
                    xhr.onreadystatechange = function () {
                        if (xhr.readyState == 4) {
                            var da = xhr.status;
                            if (da == 200) {
                                var r = xhr.responseText;
                                var rArray = r.match(/data-owner="([^ ]+)"/);
                                if (rArray.length > 0 && rArray[1]) {
                                    userid = rArray[1];
                                    console.log("[1] User id is:" + userid);
                                    plusone_tab();
                                } else {
                                    console.log("Failed parse data-owner");
                                    throw "stop execution";
                                }
                            } else {
                                console.log("Failed request mobile URL");
                                throw "stop execution";
                            }
                        }
                    }
                    xhr.open("GET", m_url);
                    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
                    xhr.send(null);
                }
            } else {
                console.log("URL path got userid");
                userid = owner;

            }
        } else {
            console.log("[0] Failed. Unable to recognize userid.");
            throw "stop execution";
        }
    }
}
if (userid) {
    console.log("[0] User id is:" + userid);
    plusone_tab();
}



步骤 5.

 然后粘贴在箭头 >> 的地方。按ENTER 键 (IE浏览器要按 CTRL+ENTER)。如下图所示。




跑了一会后,就会看见 Lady Gaga 的 +1 过的网址了。 她只 +1 过 5次。




如果没有 +1 过的话, 会显示 nothing :(





一次只显示20个网址, 如果要去下一页, 可以按最下面左下角的 "More 更多" 按钮。




其它例子, 面子书创办人。




谷歌CEO/创办人。




Kevin Mitnick 喜欢的网站都是..




Linus Torvald 推荐过的唯一一个网址竟然是...




干杯 :)