计划的今天:如果常规构想已经结束,您还可以在其他地方应用Google Apps脚本。 通过我不知道的不同语言的脚本链来自动化VPNBook的工作。 Nedo-cURL由Mikrotik提供。 为了避免在另一个地方进行电报,自我监督允许。
第1部分。无标题
一年前,我写了一条笔记“
Almost OCR来获取VPNBook密码。PHP + Mikrotik ”,内容涉及如何在Mikrotik路由器中设置自动密码检索以通过VPNBook免费访问VPN。 故事的开始就在那里。
从那以后,大量的水涌入,在俄罗斯,他们封锁了VPNBook网站,但没有封锁公开的公共VPN服务器本身。 现在,当在未通过阻止系统传递流量的服务器上启动该用于将密码的PNG图像解码为文本字符串的PHP脚本时,该脚本也应该可以工作。 但是前段时间,我尝试使用Google Apps Script(GAS)
服务script.google.com ,决定放弃外部Web服务器上的PHP脚本,将其部分或完全替换为以Web App(Web应用程序)运行的GAS脚本。 我不了解执行政策和GAS限制,但是我所做的一切都可以在免费的Google帐户中运行,并且还不需要付款。 我没有详细描述Google Apps脚本的目标。 GAS基于JavaScript语言,您可以使用第三方JS库,也可以将脚本发布为Web应用程序,未经授权即可将其提供给所有人。 当前GAS实施的功能对我来说还不够,因此我必须走出去寻找解决方法。
最初,我决定为PNG图片编写代理。 该Web脚本应该从VPNBook网站请求密码图像(我记得该密码是在PNG中发布的),并将其提供给调用此脚本进行解码的客户端。 这种绕开锁的方法。 在此符合GAS的第一个限制。 事实证明,该脚本无法呈现MIME图像/ png,而只能呈现文本格式,JSON,TEXT,XML等。 但是有一种解决方法。 您可以将PNG编码为Base64,然后将文本字符串返回给客户端。 互联网上有类似的脚本,例如
techslides.com/image-proxy-with-google-app-scripts 。 我只是简化了其中之一。 我只需要一张图片,只输出Base64字符串。 结果是一个只包含一个doGet函数的脚本-一个GET请求处理程序,该函数返回一个字符串作为响应。
function doGet() { var response = UrlFetchApp.fetch('https://www.vpnbook.com/password.php'); var b64 = Utilities.base64Encode(response.getContent());
浏览器输出示例:
iVBORw0KGgoAAAANSUhEUgAAAGQAAAANAQMAAABl11mFAAAABlBMVEX29vZMTExY89ZbAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAVUlEQVQImWNgIBrwSzCw/2ZgOADhSc5gYJCG8wxQedLdCcYFNXcgPHOZsxuSZxx7BuFZzsjdcJi34TBU5Y3cjc3IvM3McJ7kjNxtzDwwffwSIB7UTACt/h52C5DFqQAAAABJRU5ErkJggg==
接下来是PHP脚本,可以通过资源锁定将其放置在区域内的服务器上。 除了对cURL调用的参数稍作更改外,它与上一篇文章的脚本非常相似。 您需要允许cURL通过HTTP / 1.1 302临时移动,因为 调用GAS时,会将其从Web脚本地址重定向到动态临时地址:
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
和Base64解码:
$imgOCR = imagecreatefromstring(base64_decode($output));
该PHP脚本解码PNG密码,并将其作为文本字符串返回。 此外,如关于Mikrotik的第一篇文章所述。 路由器使用提取来提取密码。
结果是在Mikrotik前面的2个中间服务的工作方案。
第2部分。推进GAS。 摆脱PHP解码器脚本
在使用GAS进行实验的过程中,出现了完全放弃PHP中的密码解码器,然后在GAS中重写密码的想法。 在这里发现了一个大问题:Google脚本没有PNG处理功能,唯一可以做的就是将PNG转换为字节数组。 毫无疑问,对图像部分和像素进行了任何操作。 我爬到Github上搜索用于PNG的JS库,发现了很多:PNG.js,UPNG.js,pngjs。 有些不支持PNG像素(带有密码的图像)的1位色深。 他们沿用了各种zlib压缩库。 总的来说,这对我来说似乎有点麻烦,因此我决定只为PNG图像编写一个原始转换器,将其转换为具有通过XY坐标访问像素的功能的位图。 然后完全沉浸在PNG格式中:十六进制编辑器,阅读标准,网络上的大量描述。 最后,我进入IDAT文件的PNG部分,其中包含zlib,其中包含一个像素数组。
它需要一个解压缩zlib的功能,而该功能当然不在GAS中。 令人惊讶的是,它们具有gzip / ungzip和zip / unzip,但没有zlib。 在阅读了有关gzip(PNG格式之后的第二层沉浸度)的信息之后,我得出的结论是,尽管在那里使用zlib压缩,但不可能从IDAT部分以gzip准归档的形式组装“自行车”。 因为 要构建有效的gzip归档文件,您需要知道解压缩后的数据的长度,如果不对它们进行解压缩,我将无法获得这些数据的长度:)而且,由于长度错误,GAS认为归档文件已损坏。 最后,我转向Github,找到了一个不错的解决方案:用于Google Apps脚本库的zlib.js(https://github.com/hinimub/zlib.js/blob/develop/README.en.md)。 它是专门为通过项目密钥库集成到GAS项目而准备的。 然后难题开始收敛。 在编写了像素数组的解压缩和用于访问XY像素坐标的函数之后,便可以将解码器脚本从PHP传输到GAS。
分别计算可能的密码字符字典的哈希表。 这是我在第三方程序(在LabVIEW中,您好,同事)中执行的一次性操作。 图像中的每个字符可以分配为8位(无缩进)×10行。 1个字节足以编码一个字符行的8个像素。 您可以将像素字符串存储为整数(字节),而整个字符按10个字节的顺序存储。 结果是每个字符10个十六进制数字。 接下来,GAS解码器重复其PHP祖先。
结果是一个可以在GAS中完全运行的脚本。 function doGet() {
'A', 'FCC6C3C6FCC6C3C3C6FC': 'B', '3E63C1C0C0C0C0C1633E': 'C', 'FCC6C3C3C3C3C3C3C6FC': 'd', 'FEC0C0C0FCC0C0C0C0FE': 'E', 'FFC0C0C0FCC0C0C0C0C0':“F function doGet() {
'' C3C3C3C3FFC3C3C3C3C3 ':' H '' 7E18181818181818187E ':' I '' 1E666666466C38 ':' J '' C3C6CCD8F0F0D8CCC6C3 ':' K '' C0C0C0C0C0C0C0C0C0FE ':' L”, function doGet() {
'C3E3F3F3DBDBCFC7C7C3': 'N', '3C66C3C3C3C3C3C3663C': '0', 'FEC3C3C3FEC0C0C0C0C0': 'P', '3C66C3C3C3C3DBCF663D': 'Q', 'FEC3C3C3FEF8CCC6C3C3': 'R',“7EC3C0C07E333C37E function doGet() {
000B6DBDBDBDBDBDB ':' M '' 000DCE6C3C3C3C3C3 ':' N '' 0003C66C3C3C3663C ':' 0 '' 000DCE6C3C3C3E6DC ':' P '' 0003B67C3C3C3673B ':' Q '' 000DE736060606060' function doGet() {
该脚本仅实现GET方法。 对作为Web App发布的脚本执行GET请求时,响应将立即包含字符串形式的解码密码。
第3部分。Mikrotik和临时移动302
因此,我们有一个在外部Web App服务器上运行的脚本,该脚本独立于锁并返回纯文本密码。 似乎没有什么比在RouterOS Mikrotik中使用fetch命令进行请求更容易了。 但是,另一个惊喜等待着我。 响应于该请求(实际地址已更改),访存将返回“ 302临时移动”。
[admin@MikroTik] /environment> :put ([/tool fetch url="https://script.google.com/macros/s/A.....A/exec" http-method=get output=user as-value]->"data") failure: closing connection: <302 Moved Temporarily "https://script.googleusercontent.com/macros/echo?user_content_key=....."> 173.194.222.138:443 (4) [admin@MikroTik] /environment>
在本文的开头,我已经写过有关此的内容。 当访问Web应用程序脚本的持久已知URL时,Google重定向到一个临时URL,该临时URL随后返回对该请求的响应。 但是与PHP cURL不同,获取RouterOS不知道如何进行重定向,而是返回失败。 但是forum.mikrotik.com并没有立即发布,但是有一种解决方法。 通过在单独的任务中通过包装:execute调用异步执行,可以将标准获取输出从控制台重定向到文件。 然后,您可以检索重定向URL并使用新地址重新获取。 这在下面完成。
这是用于GAS Web App的Mikrotik脚本的全文 第4部分。电报GAS代理
我决定将这一部分投入到将Telegram服务集成到Mikrotik中的下一个迭代中。 如果不是为了阻止Telegram服务(包括api.telegram.org)而使机器人与该服务一起工作,那么在这里使用GAS纯粹是学术上的兴趣。 这个想法在文章开头重复了关于代理PNG图像请求的想法。
在这种情况下,GAS Web App将被写入代理请求,以将Mikrtotik发送给api.telegram.org。 作为基础,我从WPTelegram Google脚本
gist.github.com/manzoorwanijk/ee9ed032caedf2bb0c83dea73bc9a28e中获取了现成的脚本。 该脚本可以代理许多Telegram API方法(但不是全部)。 在args中,可以传递包含请求参数的JSON对象,例如
{"chat_id":"123","text":"HelloWorld"}
。 但是对于我从RouterOS Mikrtotik发送文本消息的任务,实现似乎很复杂,我对其进行了简化。 最终,您通常可以编写多个Web App脚本来代理各种Telegram API方法。 这是我对sendMessage方法的实现。 通过将被调用的sendMessage方法的名称甚至bot_token和chat_id嵌入到requestHandler函数的主体中,可以进一步简化该方法。
function doGet(e) { if(typeof e !== 'undefined'){ return ContentService.createTextOutput(requestHandler(e)); } } function doPost(e) { if(typeof e !== 'undefined'){ return ContentService.createTextOutput(requestHandler(e)); } } function requestHandler(e){ if (typeof e.parameter.bot_token === 'undefined'){ return 'Error! Bot token not provided'; } else if (typeof e.parameter.method === 'undefined') { return 'Error! Method name not provided'; } else if (typeof e.parameter.chat_id === 'undefined') { return 'Error! Chat id not provide'; } else if (typeof e.parameter.text === 'undefined') { return 'Error! Text not provide'; } if (e.parameter.method === 'sendMessage') { var data = { "method": "post", "muteHttpExceptions": true, payload : 'chat_id=' + e.parameter.chat_id + '&text=' + e.parameter.text } return UrlFetchApp.fetch('https://api.telegram.org/bot' + e.parameter.bot_token + '/' + e.parameter.method, data).getContentText(); } }
在Web App中发布脚本之后,可以在GET浏览器中执行请求以检查:
https://script.google.com/macros/s/A.....A/exec?bot_token=3.....3&method=sendMessage&chat_id=2.....3&text=testtext123
或在RouterOS POST请求中:
:do { /tool fetch url=("https://script.google.com/macros/s/A.....A/exec") keep-result=no http-method=post http-data=("bot_token=3.....3&method=sendMessage&chat_id=2.....3&text=testtext123") } on-error={ }
该请求被包装在错误处理中,因为,如上所示,第一次调用fetch将引发异常“ Moved Temporarily 302”,并且没有错误处理程序的脚本将在此时停止。 一个无需转发的获取调用就足以发送消息,因此,如果您不需要Telegram API返回的JSON对象,则无需进行另一个调用。
第5部分。最终
我将真正的应用程序带到了Google Apps Script与其他服务的结合处。 您可以提出更多建议。 例如,在GAS中编写一个Telegram机器人,它将通过缓存请求以VPNBook密码响应,以减少VPNBook(缓存服务)上的负载,所有这些都将在一个GAS脚本中。 您可以在GAS上编写Mikrtotik的日志记录系统或备份配置,这些记录或备份配置将放置在Google Docs和Google Sheets文件等中。