Jump to Navigation

笔记软件https协议逆向汇总

笔记软件https协议逆向汇总

为啥现在就记录未完成的工作

从现在看,预期工期特别长,先记录,再继续后续工作。

起因

最近使用evernote,更新到6.6发现不能正常同步笔记。

然后突然想自己做的笔记软件,就不会出现这种问题了。

但是,毕竟对GUI客户端开发也不熟悉,遇到UI的开发还是很头痛的。

而且如果一个笔记软件的GUI客户端做的不顺手,那是没法用的,达不到随手快速记录的目的,也就没有什么意义。这个想法基本放弃了。

后来,想起来为知笔记客户端有个开源版本,考虑着也许可以在这个开源客户端基础改一个自己的版本出来,配合自己的笔记服务端使用。

这个想法看起来不错,然后动手改为知笔记开源客户端源代码中的HTTP API 地址,用go语言的martini+xorm写了个模拟的笔记server端(当然只是个壳子),发现可行。然后从代码和试验中实现了一些为知的API模拟版本,基本实现的笔记创建,修改,删除,以及附件的功能,功能上后面再说。

从这个过程中,为了了解API的请求协议格式,还使用了抓包工具,最开始使用wireshark,使用不熟悉的上网搜索,偶然看到了抓HTTPS协议包(并解密)的工具介绍,像fiddler2/fiddler4,Charles等。

那么看完了介绍之后,当然是试验一下了。这次试验的是使用HTTPS的有道笔记最新版本,抓包软件使用的是fiddler4,在windows上。果然,发现fiddler4把所有的HTTPS请求全部解析出来了。这厉害了,那不是可以轻松逆向笔记软件的协议了吗?那不是可以使用现有的笔记客户端,对接自己的私人笔记服务端了吗?

接下来就进入标题说的HTTPS抓包并逆向协议的过程了,经过一番试验和模拟server端的对接尝试,这种HTTPS协议逆向的方式的确可行,并且实现了一部分逆向出来的API接口,让笔记客户端能够部分接入到模拟server端。

在经过了很久的尝试之后,发现现在把这些先记录下来比较,因为要做的工作实在太多了。

虽然没有完成预期的目标,也通过这个过程了解了很多不太熟悉的知识。现在觉得原来的预期目标已经不那么重要了,所以,本文整理的主要内容并不是一个笔记软件,而是HTTPS协议逆向了,其中还包括了一些代理服务器的内容。

为什么没有继续使用wireshark抓包

wireshark 当然是一个强大的抓包工具。

Fiddler2/Fiddler4 的HTTPS抓包和解密机制

Fiddler是一个应用层的专用于HTTP/HTTPS协议的强大抓包工具,其作者Eric Lawrence是Fiddler Web调试平台的创始人,是微软的IE浏览器组的Program Manager,他从1999年一直在开发Web应用和浏览器。(还是挺厉害的。)

Fiddler在使用上却比较简单,安装启动软件,默认就能够抓取本机上所有的HTTP请求了。

那么Fiddler是怎么抓包的呢?查看 Options -> Connections,发现默认监听了 127.0.0.1:8888端口。这是一个HTTP(S)协议代理端口,它会在启动时自动设置整个系统的全局代理到本端口,然后就能够抓取到请求包了。而且在Fiddler退出的时候,会改回原来的系统全局代理设置。要验证的话,可以查看IE浏览器的代理设置。

流程为: application -> Fiddler:8888 -> server

Fiddler默认没有开启HTTPS的抓包选项,这里需要自己打开这个选项:Options-> HTTPS:

此处图片 1

然后再试的时候,可以看到已经能够看到HTTPS请求的解密包了。

那么Fiddler是怎么解密HTTPS协议包的呢?简单的说,就是使用假证书,做HTTPS中间人欺骗。

当然这个证书是有要求的,这个证书要是自签的根证书,并使用这个根证书发子证书。有了根证书之后,需要把这个根证书导入到系统中的“受信任的根证书颁发机构”这个区域。

这个对于本机有管理员权限的应用也是可以自动化完全的,后面还会用到手工导入的地方。在尝试的过程中,使用自己编写的代理服务器的时候,一直提示证书不信任的客户端错误提示。之前在这个地方一直没明白,否则自己编写的代理服务器也是可以解密HTTPS协议包的了。自动化不好的地方是有时候不知道做了什么,所以很难再手动模拟出来。

导入了根证书之后,Fiddler还会在接收到请求的时候自动做一个工作,即时使用刚才的根证书为请求的域名签发一张子证书。这个证书的作用是,在Fiddler作为服务端角色时,告诉客户端该证书是服务器的证书,并使用该证书与客户端进行加密通信(当然Fiddler有证书能够解密)。客户端接到证书时验证这是一个“受信任的根证书颁发机构”签发的证书,并且证书中的域名也和自己请求的域名完全一致,所以客户端会认为这就是真正的服务器,完全感受不到证书被替换了,还会继续保持正常发送与接收请求,Fiddler从而达到解密客户端发送的HTTPS请求的目的。

证书加密解密机制过程还是挺复杂,可以多查看其他HTTPS证书相关的资料。

Fiddler实现的这整个过程有一个技术名称叫MITM(Man In The Middle / 中间人),基于这个技术有一个名称叫 man-in-the-middle attach (中间人攻击)的常用攻击方法,可以获取网络上传输流量的信息。在后面的段落中还会介绍一下MITM,但并不包括MITM attach。

好了,现在HTTPS协议解析机制已经全部完成了。

在这个过程中,被根证书不信任问题头痛了很久。因为在远程使用Fiddler时,也出现了证书不被客户端信息的从而无法解密HTTPS包的问题,一直思考为什么Fiddler在本机上时却能解密,终于在边查资料边考虑的过程中才思考明白的。

其实远程使用Fiddler之前,还尝试过自己编写的代理(当然也是远程的),也因为证书不被客户端信任问题一度放下,继续查找资料才发现Fiddler导入根证书的隐含的步骤。然后才想到把自己编写的代理的根证书导入到客户端机器上,才解决了这个证书信任的问题。后面还有更详细的自己编写的代理的说明。

关于Fiddler就说到这里,不过再补充说明一下:

如果要说明Fiddler的完整网络流程,应该是比本段开始介绍的流程还多一个环节:

完整流程为: application -> Fiddler:8888 [ -> custom proxy ] -> server

如果没有设置 custom proxy,则直接访问 server,而如果设置了 custom proxy,则会把所有的HTTP(S)请求转发到指定的代理上。

这种方式,是在还没有搞明白证书信任问题之前使用了一下,让Fiddler帮助转发已经解密的HTTPS请求到自己编写的API入口程序,从而实现模拟API的请求响应功能。虽然也能够实现自己的模拟笔记 API 服务端的功能,但是程序使用起来当然很不方便,相当于要带上这么一个中转程序和步骤。好在后来明白了Fiddler是怎么做的,模拟笔记 API 服务端才能脱离中转直接提供给现有笔记客户端使用。

MITM proxy

MITM的全称是 Man-In-The-Middle,中间人。MITM其实是一个描述型的名称,并不是一个具体的标准或者实现。

MITM还与代理服务器有关,但是还是要与代理服务器稍微区分一下。

在做HTTPS协议逆向的过程中,由于没有特别分清楚其中的区别,多浪费了很多时间。

MITM一般分透明类型(隐式代理)和普通类型(显式代理)。

常用的HTTP(S)代理,socks4/5代理可以算是普通类型的MITM,因为需要在客户端上显式地设置代理。

在逆向协议的过程中,不断切换两种 MITM 模式,希望找到合适的方式。后来考虑使用场景的不同,可能两种方式都是需要的,实现模拟 API 服务端入口的时候,顺便把两种都实现了。

MITM的透明类型方式,能够在不修改客户端的情况下使用,所以这种方式是在提到MITM时的默认方式。

这种使用方式一般需要搭配流量转发一直使用,场景如下:

现有的笔记客户端是不开源的,像有道笔记,印象笔记,无法像修改定制为知开源客户端一样修改 API 地址,达到直接访问模拟笔记 API 服务端的目的。

这时候,可以使用iptables流量转发工具,把流量转发到模拟笔记 API 服务端入口上。

iptables是最好不过,但配置运行场景更复杂,需要把本机上外出的流量做转发。还没有完全尝试成功,后续还会继续考虑这种方式的配置,毕竟使用 Linux 桌面系统比较多,并且做流量转发不需要使用额外的端口。

所有的流量都转发到MITM入口后,就可以执行类型Fiddler的中间人功能,实现HTTPS流量的解密。

目前了解的MITM实现有两个,

  • mitmproxy python语言 http://mitmproxy.org/
  • goproxy go语言 https://github.com/elazarl/goproxy

这两个都比较好用,不过最近使用go语言比较多,在实现过程使用了 goproxy 这个。

通过 MITM 机制,才能在不修改笔记客户端的情况下,实现模拟 API 服务端入口的功能。这也是在此简单介绍了 MITM 的原因。

后面还会对基于 goproxy 实现入口做介绍。

HTTPS SNI 的机制。TODO

如何直接为笔记客户端提供模拟 API 服务器功能

这里指的是现有的客户端无法修改其请求的地址,或者端口,需要想办法转发到模拟入口上。

在前面段落,提到了一种 使用 iptables 转发的方式。在这再介绍一种socat+hosts方式。

socat是一个简单好用的端口转发工具,支持TCP/UDP,由于 iptables 还不可用,本次大多数测试都是使用的这个工具。但是需要使用到额外的端口作为入口。比如有如下转发:

socat TCP-LISTEN:80,fork TCP:127.0.0.1:9080 socat TCP-LISTEN:443,fork TCP:127.0.0.1:9443

配合hosts文件修改,能够在客户端不做任何修改的情况下把流量转发到模拟入口了。

如何这两种方式,还与模拟入口是否与客户端在同一台机器上有关。以下就两种场景分别说明:

  • 客户端与模拟入口安装在不同的机器上:

修改客户端机器上的hosts文件,把笔记软件使用的域名指向服务端的IP。请确保不在该机器上使用修改的域名相应的其他服务,流量会转发到模拟入口机器上,没有实现相关功能的情况下访问不到。

在不希望使用额外端口的情况下,在服务端机器上,使用iptables把入口流量的80和443端口直接转发到模拟入口的端口上,如9080, 9443。

另外不要忘记了,在客户端机器上导入代理的根证书。

在可以使用额外端口的情况下,在服务端机器上,开启上面的两条socat进程即可。

这种情况,对客户端所在机器上的改动算是非常小了,其他的由服务端实现。

  • 客户端与模拟入口安装在同一台机器上:

这个比较复杂,因为如果在本机上修改了hosts,那么MITM入口程序接收到请求之后,如果需要再转发到原始真实服务器,则需要额外的对转发功能做修改,采用另外的域名解析系统,并正确的设置请求转发的连接IP地址才行,否则会出现请求循环转发。这是模拟入口还没实现的。

这种情况下,还是不要修改hosts。

另一个问题,如果使用 iptables 做出口流量转发,也会出现请求循环转发,需要小心的设置。

当然,如果实现了所有的 模拟 API 接口,不再需要访问上游的服务器,那么也就没有这个问题,问题是还没有完全实现,像登陆认证部分。

目前来看,修改hosts,在模拟入口里发出的请求定制DNS查询还比较可行。

TODO how

模拟 API 服务端入口

这个入口,实体为端口号,目的是实现模拟API服务端的请求接收。

目前这个入口实现了以下3个功能:

  • 9080 端口,实现透明HTTP协议的接收与转发功能
  • 9443 端口,实现透明HTTPS协议的接收与转发功能
  • 9088 端口,实现非透明HTTP(S)的显式代理功能

在入口端口接收到连接请求时,分别根据各自的功能实现相应的转发。

转发时,有两种可能的处理方式,一种直接调用相应的请求处理API函数。另一种是像Fiddler一样,再转发到其他的独立的API端口上。

现在的实现,是把 API 入口和转发放在一起,能够立即调用 API 函数,也就是第一种方式。

实现中使用了goproxy完成HTTPS的证书签发与中间人功能,这让实现此功能的复杂度降低了很多。

由于是自己实现,还附带了一个请求复制的功能,即能够把所有的请求复制出来,转发或者保存下来而不影响原有的请求链接。并且对于没有实现的 API,依旧会请求到原始真实服务上。这是通过模拟 API 返回一个特殊的 501 响应码实现的。如果模拟 API 响应的是 501,则中间人认为 API 尚未实现,依旧把请求发送的原始服务器。反之,则终止请求,使用模拟 API 响应直接返回给客户端。

另外一个附带功能,就是能够抓取请求,并采用直观的页面列表的方式展示出来,这个功能是根据mitmproxy的mitmweb启发而来的。

注意这个功能,并没有分配单独的端口,而是走的 :9080。如果为了实现的方便,不让这个附加功能影响其他的功能,其实可以使用另一个新的端口提供抓取请求的展示功能,比如 :9081。

由于抓到的包格式不一样,比如有的是直接的POST_DATA URL编码格式,有的是JSON格式,还有的是thrift二进制格式,所以要暂存,并在展示的时候做相应的格式化处理。

为了让抓取的包能够及时展示出来,本mitmweb 采用websocket与页面通信,及时通知新抓取到的请求。

目标的成果

在做模拟 API 入口的时候,为了理清思路,简单画了一个图:

此处图片2

这是一个网络图,表示 API 请求的网络处理流程。由于考虑多个笔记客户端的支持,这个图显得复杂,如果希望单独支持某一个笔记软件客户端,还是简单一些的。

除了这个,还有一些代码,大概 为知笔记的 API 实现了60%,有道笔记的 API 实现了 40%,印象笔记的 API 实现了 30%吧。实现的部分大多是和笔记,附件,tag直接相关的。剩下还有和登录相关的,只实现了很少的一部分。

可以看有一个有道笔记导出的程序,能够自动登陆到有道笔记并下载所有的笔记内容,其中涉及璚有道笔记的登陆协议过程。(这个笔记导出程序需要用户名密码,是完全的登陆模拟,并且也只实现了下载笔记后输出的终端的功能,如果需要转换成其他的格式,或者导入到其他的笔记,还需要进一步的开发。)

可以说,解密了HTTPS信息后,发现这3个笔记软件的登陆过程都是非常自然直接,也很容易实现。其中关于这一点也有一些思考,其实也比较容易防止,至少能够不要这么直接的暴漏出来。这在后面的说明时再进一步讨论。

关于这个成果代码的问题,目前由于涉及试验性的东西太多,代码也还比较乱,需要整理一下再发布出来。如果有兴趣有问题需要咨询,可以直接联系。

有道笔通信 API 记协议简述

登陆方面:有道登录过程还有客户端与客户端的额外加密和验证,没有加密算法无法模拟的。目前只能使用抓取的包,能够使用一会的时间。

完整同步,

首先请求下载一个完整的列表数据,然后每个笔记的内容和附件部分单独请求下载。 忘记有没有分页了。我测试的400多个笔记列表是一次性下载的,不过也可能是按500分页的。

单个笔记上传,

POST同时提交笔记的基本元信息,以及笔记的内容数据。这个API这格式看着真是难看,其格式为,POST数据的前半段是元信息JSON,JSON之后是笔记的内容数据。

附件上传,

在笔记有附件的情况下,会选择单独上传附件,由客户端决定。附件的上传格式与笔记一样,整个数据是元信息JSON和附件内容。要注意的是JSON与之后的数据并没有什么显式的分隔符,需要通过JSON读取来确定之后数据的开始位置。

多设备编辑,

  1. 有一个与服务器的longpull http请求,大概能够连接30秒,通过这个连接获取服务器端推送的数据。
  2. 多人同时编辑,未知。

笔记的历史版本,

好像有道 笔记的版本是按账号递增的,而不是按照每个笔记单独使用一个版本号。

现在实现有道服务器端逻辑的模式基本有了,后面就是细化每一个接口调用的逻辑了。

在以上协议的基础上,测试过程中还实现了一个有道笔记的全量导出程序(目前下载简单保存到文件,每个笔记一个文件)。该程序是一个命令行工具,实现了一大套完整的有道登陆逻辑,需要用户名与密码,并且如果是在需要验证码的时候,存储验证码为图片文件,并用浏览器打开,同时在命令行提示输入验证码,再次尝试登陆。登陆成功后,实现了笔记全量下载的接口调用逻辑,大概有3,5个HTTP API请求。

开放接口,

有道笔记的开放接口,其功能的丰富程序与正式版本中的接口差太多,只是一个非常小的子集,而且开放接口的API与正式版本使用的API完全不同,应该是重新封装过的。

接口API详细文档: coming soon...

为知笔记通信 API 协议简述

登陆方面:这个是做的比较一般的,完全靠HTTPS的加密还处理加密问题,在抓取到HTTPS包的情况下,可以看到密码是明文的。

完整同步下载,

按照分页方式下载笔记列表,默认每页200个记录。然后每个笔记的内容单独下载。

单个笔记上传,

整个请求的body部分使用xml格式,自带笔记元信息,笔记正文数据部分采用base64编码,而编码的内容则是笔记整个目录的压缩数据,可能包含html,css, js, 图片。采用base64编码能够处理二进制数据,并且减少一些数据传输。还要注意在文本长的时候,为知上传接口支持分块上传。

附件上传,

附件部分采用另外的标识的接口,但是格式与笔记接口类似。同样也是使用xml请求,xml包含的数据项类似但不完全相同。同样也支持二进制数据和分块传输。

多设备编辑,

未知。

笔记的历史版本,

支持

接口API详细文档: coming soon...

开放接口,无

印象笔记通信 API 协议简述

印象笔记的通信数据格式为 HTTPS+thrift,而其他多为HTTPS+JSON的。thrift这种格式是二进制格式,每次传输没有字段名,稍微节省点流量。这种格式如果要在没有idl文件定义的情况下反解格式是比较麻烦的,好在后来发现印象笔记还是比较开放的,这些接口的idl定位文件全是开源的。

登陆方面:虽然通信协议是thrift二进制的,但这个是做的还是比较一般的,完全靠HTTPS的加密还处理加密问题,在抓取到HTTPS包的情况下,可以看到密码是明文的。

完整同步,

一次请求下载整个笔记列表信息,再通过下载接口逐个下载笔记,每次请求下载一个笔记。

单个笔记上传, 这是evernote API接口是复杂的一个,包含了note的元数据信息,note的正文,note的附件。

参与:http://dev.evernote.com/doc/reference/NoteStore.html#Fn_NoteStore_createNote

附件上传,

是笔记创建接口中的可选结构。

多设备编辑,

支持

笔记的历史版本,

支持

开放接口, 在分析完其通信协议之后,就可以查阅dev.evernote.com的接口说明了,参数的结构和API与开放接口是一致的。

小结

关于https的协议逆向是在准备做笔记后端模拟实现的时候碰到的,觉得可以记录以务后续参考,所以才有了这篇博文。

在完全基本的通信协议API规格分析之后,接下来就是继续之后的工作,不过工作量还是相当大的,这个感觉算是挖了个大坑。后续进展暂无决定,也不知道是否有人对这个实现笔记模拟后端有兴趣,如果人多的话也许可以再进一步搞起来。

这篇文章最后一部分各笔记的协议整理是在开始分析协议之后一个多月时间内陆续整理的,时间跨度有点长,记录的详细程序还有欠缺,记不太清楚了,感觉有点烂尾了。后续如果再有时间继续的话,再进行详细的补充。

Add new comment

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Enter the characters shown in the image.


Main menu 2

Story | by Dr. Radut