0×00 前言
众所周知,最近谷歌被封堵的很厉害,什么Gmail啊、谷歌地图啊全都无法使用。当然对我们在校的学生来说,这些用不了我也就忍了,但是谷歌学术用不了你让我怎么搞科(chao)研(xi),I cannot endure!
好在办法总是有的,代理、VPN、GoAgent(最近好像也用不了了),这些东西小伙伴们应该都非常熟悉。但是俗话说的好,“天下没有免费的午餐”,如果你用的是免费的代理或者VPN,那速度简直无法直视,相信这点小伙伴们也深有体会。那么有没有一种配置简单、速度流畅的方法呢,当然有了,这就是今天被拿来开刀的主角——红杏,这货居然还是邀请机制,前面那个链接是我的邀请链接,大家如果付费的话就便宜我了哦(^_^)
0×01 初识红杏
其实在我看来,“科学上网”并不是什么很复杂的事情,代理、VPN的配置方法对一个爱折腾的人来说应该是非常简单的事情,所谓配置复杂不过是懒人的借口罢了。红杏其实也没有官方宣传的那么神奇的,只不过是赚一些懒人的钱罢了。相信看完这篇文章之后你也能有这种感觉~
不过这里不得不说红杏的宣传主页做的还是非常炫的,是基于CSS的一个网页幻灯片,如果感觉兴趣的话可以谷歌搜索reveal.js。好了我们言归正传,红杏其实就是Chrome的一个插件,如果你从刚才选择了下载离线安装包的话,就得到了一个crx格式的文件,安装方法也很简单,在Chrome的地址栏输入chrome://extensions/,然后将crx文件拖动到这个页面上就可以了,之后注册一个账号,登录成功之后就可以看到下面这个页面了:
红杏的具体使用方法官网已经讲得很详细了,由于我是非VIP用户,因此不能添加自己的科学上网列表,不过谷歌学术已经可以正常访问了~
0×02 红杏的原理
那么红杏到底是怎么工作的呢,它的原理是什么呢?下面我们先通过Wireshark一探红杏背后的秘密。启动Wireshark进行抓包,然后用Chrome访问Google,在Wireshark中得到如下结果:
熟悉openSSL的同学一定对这些数据包不陌生,虽然说Wireshark支持对SSL协议进行解密,但是前提是你得有服务器的私钥啊,只恨当初openSSL爆出漏洞时没有行动(>_<)
既然没有私钥,显然用Wireshark抓包的方法是行不通了,那么我们换个思路,从Chrome浏览器入手,利用Chrome自带的开发人员工具进行尝试。
登录过程
由于最近对登录过程比较感兴趣,因此先从登录过程入手了。先进入红杏的登录界面,从登录界面可以看出它是先验证用户名是否有效:
然后按F12打开开发人员工具,切换到Network选项卡,输入用户名之后点击登录,得到如下结果:
响应非常简单{"exists": true},表示该用户存在 接着输入密码,注意勾选Preserve log,否则页面跳转之后就无法看到数据包了,登录成功之后得到如下数据包:
采用的是HTTPS协议,因此明文密码也就无所谓了(-_-),响应如下:
{
"name": "test@test.com",
"level": null,
"no_password": false,
"anonymous": null,
"sid": "DA1D666B-20140627-xxxxxx-xxxxxx-xxxxxx",
"inviter": "someone@test.com",
"until": null
}
大部分的value看key的名字就知道是什么含义,这里解释一下level是红杏用来标识用户VIP等级的一个字段,免费用户、包月、包年用户的值是不一样的,后面分析红杏的原理时还会涉及到这个level。sid是红杏为每一个用户生成的ID号,其中还可以看到注册日期。
代理过程
好了,分析完登录过程下面来看看红杏是如何实现代理的,打开一个新的标签页和开发人员工具,进入谷歌首页,可是却发现开发人员工具的Network选项卡中只有google.com数据包,没有和代理相关的线索:
其实也没有什么好惊讶的,开发人员工具毕竟不是万能的,主要是供前端工程师使用的,代理的过程对它来说是透明的,我们自然就看不到代理的过程。
那么怎么办呢,Wireshark和开发人员工具都无能为力,只能用“逆向”的手段了,之所以打上引号是因为并非真正的逆向。说起逆向,小伙伴们是不是立刻想起了神器Ollydbg和IDA?不过crx文件的逆向可没有那么复杂,因为这货就是个特殊的zip文件,把之前下载的hongxin.crx改成hongxin.zip并解压就得到了下面的这些文件:
其中文件夹的名称已经告诉了我们里面文件的作用,这里也就不再啰嗦了。红杏插件的主要逻辑代码都在js文件夹中,里面全是一些javascript文件,一开始我还以为这些js文件经过了混淆(就是把里面的函数名和变量名替换成“火星文”),后来发现只是做了简单的eval编码(严格来说不能算加密)和压缩,用一个Javascript在线工具,点击解密即可把代码还原。
还原完代码后,我们怎样从这么多js文件中找到与代理过程相关的那个文件呢?一般来说,文件名很大程度上反映了它的功能(程序员都这么干,除非你跟自己过不去或者和团队的人过不去),所以我们直接搜proxy,在js\services\目录下找到了一个proxyManager.js,直觉告诉我们就是它了!代码有大概400多行,这里我贴几段比较重要的代码段:
generateAndApplyConfig = timeUtils.throttle(function() { var config; if ($rootScope.mode === MODES.NEVER || $rootScope.user.role === ROLES.GUEST) { chrome.proxy.settings.clear({}) } else { config = { mode: 'pac_script', pacScript: { data: generatePacScript() //由generatePacScript()函数生成pac脚本 } }; chrome.proxy.settings.set({ //看到这里大概就知道和代理设置相关了 value: config,//config前面已经赋值了 scope: 'regular' }, (function(_this) { return function() { return null } })(this)) } return $log.debug(me, '_generateAndApplyConfig') }, 500); /* 生成pac脚本函数 */ generatePacScript = function() { var domain, i, lines, mode, node, part, parts, proxy, proxyString, proxyStrings, reversedDomainTree, scheme, source, _i, _j, _k, _l, _len, _len1, _len2, _ref, _ref1, _ref2; mode = $rootScope.mode; if (mode !== MODES.AUTO && mode !== MODES.ALWAYS) { mode = MODES.AUTO } mode = MODES.ALWAYS; //把这句话加上就可以实现“一直模式”,非VIP用户也可以哦~ proxyStrings = []; _ref = $rootScope.proxies || []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { proxy = _ref[_i]; scheme = proxy.scheme; proxyStrings.push("" + scheme + " " + proxy.host + ":" + proxy.port) } proxyString = proxyStrings.join(';'); /* 下面就是生成pac脚本的部分了 */ lines = []; lines.push(['function Find', 'roxyForURL(url, host) {\n'].join('P')); lines.push("var D = \"DIRECT\";"); lines.push("var p='" + proxyString + "';\n"); lines.push("if (shExpMatch(host, '10.[0-9]+.[0-9]+.[0-9]+')) return D;"); lines.push("if (shExpMatch(host, '172.[0-9]+.[0-9]+.[0-9]+')) return D;"); lines.push("if (shExpMatch(host, '192.168.[0-9]+.[0-9]+')) return D;"); lines.push("if (dnsDomainIs(host, 'localhost')) return D;\n"); lines.push("if (url.indexOf('https://www.google.com/complete/search?client=chrome-omni') == 0)"); lines.push("\treturn D;"); lines.push("if (url.indexOf('http://clients1.google.com/generate_204') == 0)"); lines.push("\treturn D;"); lines.push("if (url.indexOf('http://chart.apis.google.com/') == 0)"); lines.push("\treturn D;"); lines.push("if (url.indexOf('http://toolbarqueries.google.com') == 0)"); lines.push("\treturn D;\n"); lines.push("if (url.indexOf('_HXPROXY=') >= 0) \treturn D;\n"); for (_j = 0, _len1 = WHITE_LIST_DOMAINS.length; _j < _len1; _j++) { domain = WHITE_LIST_DOMAINS[_j]; lines.push("if (dnsDomainIs(host, '" + domain + "')) return D;") } lines.push("\n"); if (mode === MODES.AUTO) { reversedDomainTree = {}; _ref1 = domainManager.domainNames(); for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { domain = _ref1[_k]; node = reversedDomainTree; parts = domain.toLowerCase().split('.').reverse(); for (i = _l = 0, _ref2 = parts.length - 1; 0 <= _ref2 ? _l <= _ref2 : _l >= _ref2; i = 0 <= _ref2 ? ++_l : --_l) { part = parts[i]; if (i === parts.length - 1) { node[part] = 1 } else { if (node[part] === 1) { break } if (node[part] == null) { node[part] = {} } node = node[part] } } } lines.push("var node = " + (JSON.stringify(reversedDomainTree)) + ";"); lines.push("var hostParts = host.toLowerCase().split('.');"); lines.push("for (var i=hostParts.length - 1; i >= 0; i --) {"); lines.push(" var part = hostParts[i];"); lines.push(" node = node[part];"); lines.push(" if (node == undefined || node == 1) break;"); lines.push("}"); lines.push("if (node == 1)"); lines.push(" return p;\n") } else { lines.push('return p;') } lines.push('return D;'); lines.push("}"); source = lines.join('\n'); alert(source); //比较初级但是非常实用的调试方法,直接用alert显示source的值 localStorage.setItem("pac", source); //比较“高大上”的方法,利用HTML5的Local Storage可以直接把source保存下来,对应的键为pac return source };
source = lines.join('\n'); alert(source); //比较初级但是非常实用的调试方法,直接用alert显示source的值 localStorage.setItem("pac", source); //比较“高大上”的方法,利用HTML5的Local Storage可以直接把source保存下来,对应的键为pac return source};
其实看到chrome.proxy.settings.set就知道这肯定和代理设置相关了,在谷歌上一搜就找到一篇关于开发Chrome代理扩展程序的文档,里面说的非常详细。注意后面的generatePacScript函数,其生成了一段pac脚本(关于pac脚本的知识刚才的那篇文档里也有涉及),其实就是一段简单的程序,告诉浏览器访问哪些网址的时候用什么代理。这段pac脚本就是红杏的“秘密”,如下:
function FindProxyForURL(url, host) {
var D = "DIRECT";//直接连接,不使用代理
var p = 'HTTPS test1.com:443;HTTPS test2.com:443';//使用代理的方式、主机、端口号
/* 下面的代码主要是设置一些规则,像127.0.0.1、localhost这样地址不走代理 */
if (shExpMatch(host, '10.[0-9]+.[0-9]+.[0-9]+')) return D;
if (shExpMatch(host, '172.[0-9]+.[0-9]+.[0-9]+')) return D;
if (shExpMatch(host, '192.168.[0-9]+.[0-9]+')) return D;
if (dnsDomainIs(host, 'localhost')) return D;
if (url.indexOf('https://www.google.com/complete/search?client=chrome-omni') == 0) return D;
if (url.indexOf('http://clients1.google.com/generate_204') == 0) return D;
if (url.indexOf('http://chart.apis.google.com/') == 0) return D;
if (url.indexOf('http://toolbarqueries.google.com') == 0) return D;
if (url.indexOf('_HXPROXY=') >= 0) return D;
if (dnsDomainIs(host, '0.0.0.0')) return D;
if (dnsDomainIs(host, '127.0.0.1')) return D;
if (dnsDomainIs(host, 'localhost')) return D;
if (dnsDomainIs(host, 'ddparis.com')) return D;
/* 下面的代码就是红杏的访问控制了,当你访问google.com、twitter.com这样的域名时就会自动用代理 */
var node = {"net":{"akamaihd":1,"facebook":1,"fbcdn":1,"cloudfront":1,"sstatic":1,"doubleclick":1,"2mdn":1},"com":{"facebook":1,"twitter":1,"twimg":1,"google":1,"googleusercontent":1,"googleapis":1,"gstatic":1,"gmail":1,"tumblr":1,"appspot":1,"amazonaws":{"s3":1},"blogspot":1,"blogger":1,"mediafire":1,"googlevideo":1,"wordpress":1,"vimeo":1,"googlesyndication":1,"ggpht":1,"imgur":1,"googleadservices":1,"cloudflare":1,"deghhj":1},"co":{"t":1},"hk":{"com":{"google":1}},"in":{"honx":1},"ly":{"bit":1},"be":{"youtu":1}};
var hostParts = host.toLowerCase().split('.');
for (var i = hostParts.length - 1; i >= 0; i--) {
var part = hostParts[i];
node = node[part];
if (node == undefined || node == 1) break;
}
if (node == 1) return p;
return D;
}
事实上我们把这段脚本保存下来,使用另一款Chrome扩展SwitchySharp,并将这段脚本导入到SwitchySharp中同样可以实现代理功能!不过在访问一些没有使用HTTPS协议的网址如http://scholar.google.com时会出问题(暂时还不清楚原因-_-),但是像https://www.google.com或者https://twitter.com这样的网址是没有问题的~
获取代理服务地址过程
刚才那段pac脚本中最重要的内容当然是代理服务器的地址了,那么好奇的小伙伴们一定想知道红杏插件是从哪里获取这个地址的呢?如果是固化在代码里面的话岂不是可以知道红杏插件所有代理服务器的地址(包括传说中VIP用户专享的代理服务器),当然这只不过是我们一厢情愿的YY,除非程序员的脑子进水了,不然怎么可能把这么重要的秘密写在程序里,而且万一哪天服务器宕机或者换地址了,岂不是要重写程序?所以可以肯定代理服务器的地址一定是动态获取的。通过反复的阅读代码和调试,终于找到了这个关键代码段,下面简述一下过程。
首先,在js\app.js中发现了这样两行代码:
SERVER = 'ddparis.com'; API_URL = "wss://" + SERVER + ":443/red/extension";
猜测API_URL可能和获取一些参数有关。注意wss://,这个应该称之为安全的WebSocket协议,与之相对应的是ws://(类似http://和https://的关系),关于WebSocket的知识可以参考维基百科的解释,简单来说就是一种客户端和服务器快速交互数据的方式。WebSocket一旦建立连接,服务器便可直接向客户端发送消息,与传统的HTTP请求/响应式的协议是不一样的。
接着,我在js\services\server.js中发现了这样一行代码:
client = RedSockClient.create(API_URL);
这行代码以API_URL作为参数并将线索指向了js\services\RedSockClient.js这个文件,从名字能大概能猜出是和服务器通信相关的,其中有如下代码段:
/*onmessage函数在WebSocket中用来处理服务器发来的数据包*/
_ws.onmessage = function(e) {
var ackId, callback, callbacks, data, messageType, name, _i, _len, _ref, _results;
if (e.data !== '2') {
$log.debug(me, '>>>', e.data)
}
_lastBeat = timeUtils.milliTime();
/*服务器发来的消息解码后赋给_ref*/
_ref = decodeMessage(e.data),
/*messageType的值有4种,从下面的switch-case中可以看出来*/
/*当messageType为MT_EVENT时,name的值有3种,profile、proxies、tasks,data的值与name相对应*/
/*当然这里我们最关心的是当name为proxies时,data里面的值是什么*/
messageType = _ref[0], ackId = _ref[1], name = _ref[2], data = _ref[3];
switch (messageType) {
case MT_DISCONNECT:
return _this.disconnect();
case MT_HEARTBEAT:
return null;
case MT_EVENT:
callbacks = _onMap[name];
alert(name);//查看name的值
var stringdata = JSON.stringify(data);//由于data是一个Object List,这里将其转换为字符串
alert(stringdata);//查看data的字符串形式
if (callbacks && callbacks.length > 0) {
_results = [];
for (_i = 0, _len = callbacks.length; _i < _len; _i++) {
callback = callbacks[_i];
if (typeof callback === 'function') {
if (ackId) {
var stringzz = JSON.stringify(callback(data));
_results.push(_ack(ackId, callback(data)))
} else {
_results.push(callback(data))
}
} else {
if (ackId) {
_results.push(_ack(ackId))
} else {
_results.push(void 0)
}
}
}
return _results
}
break;
case MT_ACK:
return typeof _ackMap[ackId] === "function" ? _ackMap[ackId](data) : void 0;
default:
return $log.error(me, 'Invalid MessageType:', messageType)
}
};
这里我采用了一个笨方法alert()进行调试,得到如下结果:
因为代理服务器的地址是由ddparis.com这个服务器发到红杏上的,所以除非通过欺骗ddparis.com的方式,否则很难拿到VIP用户专享代理服务器的地址,但是具体怎么欺骗还需要分析它们之间的通信过程,这个以后可以进一步分析。
0×03 红杏的破解
人总是在追求完美的路上不断成长的,看着刺眼的非VIP心里还是有那么一点不舒坦的,事实上通过前一个部分对红杏原理的分析,可以有N种方法破解红杏,当然也只是针对其功能限制的破解,想要拿到VIP服务器的地址可以掏10块钱买一个月的VIP,然后按照前面的方法就可以获取到了(-_-)
限于篇幅,这里提供一种最简单的办法。在前一个部分的js\services\RedSockClient.js中,有name=_ref[2],刚才我们说了_ref[2]为proxies的情况。当_ref[2]的值为profile时,data的字符串形式如下:
是不是又看到了前文提到的level?再看js\services\userManager.js中的如下代码段:
this.load = function(data) {
$rootScope.user.profile.sid = data.sid;
$rootScope.user.profile.name = data.name;
$rootScope.user.profile.anonymous = data.anonymous;
$rootScope.user.profile.no_password = data.no_password || false;
$rootScope.user.profile.until = data.until || data.vUntil;
$rootScope.user.profile.level = data.level || data.vLevel;
/*通过data.level来判断用户身份*/
if (data.level) {
$rootScope.user.role = ROLES.VIP
} else if (data.name) {
$rootScope.user.role = ROLES.USER
} else {
$rootScope.user.role = ROLES.GUEST
}
$rootScope.user.role = ROLES.VIP//加上这行代码就直接“变成”VIP了(-_-)
return $log.info("" + me + "user load from :", data)
};
结合代码和data的格式我们可以知道红杏是如何判定用户身份的,破解也非常容易,在return语句前加一句$rootScope.user.role = ROLES.VIP就行了。重新加载插件后你就可以发现讨厌的非VIP用户已经没有了,也可以开启“一直模式”和编辑科学上网列表了。
0×04 其他细节
Chrome插件调试
好吧,发现说了这么久一直没有讲怎么调试Chrome插件,其实非常简单,把crx文件改成zip后缀并解压到一个文件夹中(注意该文件夹以及其父目录最好都不要有中文),在Chrome的地址栏输入chrome://extensions/,然后勾选开发人员模式,点击加载正在开发的扩展程序,选择刚才解压的那个文件夹即可。对代码修改之后,重新加载一下插件或者重启Chrome都可以,然后就可以观察修改的效果了:
Chrome插件打包
修改完成之后,打包也非常简单,还是刚才那个页面,点击打包扩展程序,选择扩展程序所在根目录,点击打包扩展程序即可,最后会生成一个crx文件和一个pem文件(私钥文件)
将自己DIY的插件添加到白名单
Chrome从33开始,就不在再支持安装第三方插件,不过我们可以直接拖拽刚才生成的crx文件到 chrome://extensions/来突破安装,但是很快发现google play可以通过检测,发现我们修改过的扩展程序不是在应用商店下载的而直接把插件停用,且无法启用,表现为灰色,只能删除。具体解决方案可以点击这里,其中用到了一个chrome.adm文件,可以点击这里下载
0×05 总结
看到这里,你是不是也觉得红杏插件没什么神秘的呢?下面我们回顾一下关键点:
1.SSL是一个非常安(fan)全(ren)的协议(-_-)
2.CRX文件→解压→源代码(-_-),Javascript代码能混淆还是混淆一下吧
3.PAC脚本可以用于配置浏览器的代理,IE和火狐也支持哦~
其他一些东西,像HTML5、Local Storage、WebSocket还是很有意思的,小伙伴们有兴趣可以多关注一下~
0XFF 扩展阅读
下面是与本文相关的一些文章,值得一读:
*作者:bindog,原文链接:http://bindog.github.io/blog/2014/07/03/analysis-and-hack-of-hongxin
-
无语了一年前的东西还放出来.....早就不能用了
-
同学们,我来澄清一下,这是我去年写的东西,可能是FB为了凑一个专题弄过来的,并不是我自己上传的。这篇文章对应的红杏版本是2.4.7,网盘地址:http://pan.baidu.com/s/1sjJM8w9,密码:cf8u,想研究的同学可以自己看看对照一下。楼上的同学们说的对,破解是不对滴,研究它本来就是一个学习的目的。顺便说一句,我自己现在用的是shadowsocks,在网上买的账号。
-
。。人家赚点钱容易么。一直破解来破解去的。。。裱砸就不能支持一下作者
不容错过
- 检测一下你的专业指数:2015年十大测试工具你认识几个?FireFrank2015-12-29
- 追踪、定位、监听一个也不能少:最强悍的监控间谍软件FlexiSPY源码泄露Sphinx2017-04-24
- 国内影响已扩散,利用“NSA武器库”中漏洞的Wannacry蠕虫勒索软件袭击全球(含修复方案)AngelaY2017-05-13
- FreeBuf 2017互联网安全创新大会视频&图文直播(已结束)kuma2016-12-29
0daybank
已有 76 条评论
图片不错
mark
niu bi
666
那是不是文中的破解方法已经不管用了?
猎豹浏览器那个红杏出墙要买会员,不是免费的。
转发微博
。。人家赚点钱容易么。一直破解来破解去的。。。裱砸就不能支持一下作者
@ Window 支持
@ Window 你就是那个程序猿吧…..
无语了一年前的东西还放出来…..早就不能用了
10块钱而已[微笑]不差钱的还是支持下吧。这个看有机zz还是挺快的
表示已经找到了可以流畅运行谷歌学术的hosts了[doge]
不懂,只能找现成的 转发微博
打不开
google走不走都能养活一批人
没必要吧!大家也不容易!
然并卵
自己搭建一个vpn多方便啊
我这儿还有10天的VIP
VPN挂代
你干嘛老换名字
好东西
低调收藏
留下来好好看看
点击下载?
小编呢,我不想说什么,但“谷歌搜索reveal.js”………………………红杏是用来翻墙的,但是找红杏也需要翻墙………我想说一个翻墙软件需要翻墙才能找到……它有卵用啊
@ 乌列 红杏的宣传主页做的还是非常炫的,是基于CSS的一个网页幻灯片,如果感觉兴趣的话可以谷歌搜索reveal.js
楼主是说对红杏的CSS 感兴趣的人 用谷歌搜索reveal.js ……
建议你还是先用百度搜索一下reveal.js 再来喷
没事我就喜欢赞
Reply:不喜欢那个了就换了
还是喜欢LGM的付费VPN,一键就好了,看这原理头晕
有意思吗。插来插去。插上就稳定一些。。老这会员那会员的。拔出来再插件
为了不能让国内民众看到某些新闻什么的,更好的实现民众圈养。说好的自由呢?
@ Fuailen 唉,你懂的。
10元不贵,不缺钱还是支持下,服务器都要钱不然又毁一个工具
收费的。速度还可以
红杏出墙能几何???
收费的···
为了省10块钱的会员费,居然把一个插件给破解了。。。什么叫第一生产力[doge]
我去,你怎么能这样,人家辛辛苦苦写的插件,你竟然为了10块钱就破解,就不能支持一下作者么?真是的,破解了也不发出来,地址呢?????
微信看到了不
m
具体解决方案可以点击这里,其中用到了一个chrome.adm文件,可以点击这里下载
这特么是完全复制粘贴的,连下载地址都不给。
谢谢!!!
翻墙
你才用啊
chromedga 随便翻
破解当做个人爱好自己玩可以 但是不为别人的劳动力买单并不好 10元一个月并不贵 赚钱了操作系统也转正
快来抢红包吧
js的混淆是什么鬼。那个是压缩
回去试试
麻烦你也破一破QQ会员等之类同价位付费服务。