ttimasdf 发表于 2020-8-5 23:09

使用 mitmproxy 快速搭建破解软件网络验证API——以某音频处理软件为例

在测(po)试(jie)软件时,我最不愿意遇到的情况,就是目标软件有联网验证的功能。说要改代码吧,又不知道代码里在哪儿有暗桩,改掉所有的输入输出点也麻烦的很。说要断它的网吧,又不是所有软件都提供离线激活的功能。今天我介绍一种四两拨千斤的联网验证绕过方式,不用断网,字节码改动少,外加Python不到100行。~~不流失,不蒸发,零浪费~~。

最近工作开会总要用企业微信在线会议,得在电脑上投屏,还得开麦(不能多点登录所以也不能用手机的麦),电脑麦克风音质实在是太垃圾了,还有环境噪音,搜来搜去找到一个软件Krisp,在声卡驱动层做音频处理,全局降噪,非常贴合我预想的应用场景,~~只可惜要钱~~。囿于网上没有破解,只能动用我们的聪明才智来“学习”一下了。

# 应用分析

逆向第一步,我们得先知道,我们要逆向的软件用的是什么编程语言。Krisp这个软件很简单,打开安装目录,从 `*.exe.config` 文件和它引用的 `Newtonsoft.Json` 库就知道,这个软件是用C#编写的。

![.NET程序特征](https://z.cdn.rabit.pw/imgs/2020/20200705233852.png)

是C#就简单了。直接拖进dnSpy打开即可。这工具比当年的ILSpy强大了很多,能反编译,能调试,还能直接用C#改程序逻辑。而且很多.NET框架的程序发布出来都不带混淆的,变量、方法名写的清清楚楚,用dnSpy反编译一下,基本就和看程序源代码没有太大区别了。

!(https://z.cdn.rabit.pw/imgs/2020/20200706200845.png)

在dnSpy里搜搜常见的关键字,通过关键字 trial (试用),很快就能定位到 `Krisp.UI.ViewModels.KrispAppPageViewModel.UserProfileInfoChanged` 函数,这个函数中除了trial还有一个模式叫unlimited。直接用dnSpy无脑给他改了。

!(https://z.cdn.rabit.pw/imgs/2020/20200706201731.png)

嗯,至少界面上的试用限制已经不见了,表面上已经是尊贵的VIP客户了。

!(https://z.cdn.rabit.pw/imgs/2020/20200706201936.png)

但这还不够,因为这只是程序里和UI相关的一个逻辑判断,类似的修改点肯定还有很多。很多逻辑判断都是通过和常量的对比实现,既不好找,而且一一修改也太麻烦了。

所以我们转换一下思路,程序授权状态是从网络下发的,那我们不妨以网络通信作为切入点。用我们上篇文章提到的Fiddler抓包看看,还是很简单,随便翻翻就能找到 `https://api.krisp.ai/v2/user/profile/2/1` 这个API。

!(https://z.cdn.rabit.pw/imgs/2020/20200706202743.png)

那完事咱得给它改成啥呢?第一,JSON中 `data.mode.name` 这个参数得给它改成 unlimited 。第二,下面 `data.settings` 里有一大堆 `available=True` 的参数值,等试用期过后这些肯定就变成 False 了(我注册的账户还没过试用期,所以只是猜测)。所以我们截获数据修改JSON时,将这些值固定成现在的样子就好了。

# 破解方法对比

好了,我们最喜闻乐见的“为什么不用xxxxx”环节又来了。今天没有很多的选手参赛,~~况且我也没特意去搜~~。简单对比一下。

**方式1:修改API网址为自建API,进行MITM**(最终选择方案)

优点:在程序中需要修改的点较少,只要把网址字符串改一下就行。如果能构造一个长度相同的网址,甚至不用反编译工具去改源程序,直接字符串替换就行了。程序后续更新后,维护破解版的工作量非常小。

**方式2:修改客户端,在REST API请求点对返回值进行修改**

缺点:破解新版本还得重头把所有步骤来一遍。而且不同API的请求可能还在不同的Class里面。光破解一次就很费精力了。

**方式3:改hosts截获API请求**

不可行,生产环境的API访问都是https的。这种又不是传统的MITM攻击,不能搞sslstrip。断网就没办法正常登录使用,正常功能都被破坏了。



而搭建API用的框架,就是 ~~上篇文章里我说它很垃圾的~~ mitmproxy。顾名思义,这框架就是用来做中间人的。它与其他编程语言库最大的差异是,它提供的是 *修改* Web请求的API(接近其他框架中middleware的功能),而其他Web框架提供的是 *解析*Web请求的能力。比如Django,它把Web请求解析出字段,丢给我们,这个请求就发不出去了。如果我想把这个请求再发出去,就得把所有字段再拼起来,调用requests发出去。 如果目标应用有几十个API怎么办?麻烦不?我们写代码,只处理最重要的逻辑,不写无聊的CRUD,真正做到,不流失,不蒸发,零浪费!

其他MITM的工具在这里就不适用了。别的工具能放在docker容器里24小时跑在VPS上么?

# Mock API 搭建

我们搭建的这个山寨API,最重要的功能是能够让我们能够以类似编写middleware的方式对传入的请求、返回数据进行修改,同时没修改的API原封不动的透传回去。当然,写的代码越少越好。

既然明确了原理和工具,我们照着文档写代码就是了。不过说到文档,我得提醒一下,mitmproxy它压根就没有什么文档(微笑)。根据作者的经验,项目Github中的一些示例脚本,和 pydoc 命令行是两类比较不错的参考资料。按图索骥勉强也能写。

我们在运行 mitmproxy 时,需要用到它的reverse proxy模式,在这个模式下mitmproxy会接受普通的HTTP请求作为输入,而非特殊的HTTP Proxy请求。完整的运行参数如下:

```sh
mitmproxy -p 8081 -s krisp.py --mode reverse:http://localhost --set keep_host_header
```

其中,`reverse:` 后面的主机名称是随便输的,配合 `keep_host_header` 选项,同时在代码里手动将请求Header中的Host字段设置为 flow 的目的主机,只有这样才能实现在同一个端口**同时反向代{过}{滤}理多个目的主机**。

下面一段代码实现的功能:

1. 重定向 `api.krisp.your.domain` 请求到 `api.krisp.ai` ,实现反向代{过}{滤}理。
2. 将 `analytics.krisp.ai` 的所有请求返回虚假的返回值。
3. 对其他 host 返回404。

![代码片段:修正请求目的主机](https://z.cdn.rabit.pw/imgs/2020/20200706225012.png)

上段代码中我们定义的 `request` 函数就是 mitmproxy 提供的事件API,每条请求发出之前,这个函数就会被调用。接下来我们再实现另一个 `response` 函数,这个API会在mitmproxy获得Web请求的返回数据后触发(与上篇文章中的思路相似)。

而我们要做的事情很简单,将我们前文所说的部分——对 `data.mode.name` 和 `data.settings` 的修改全部实现即可。

![代码片段:模拟付费用户取值](https://z.cdn.rabit.pw/imgs/2020/20200706230625.png)

最后,按代码示例里的格式,定义好class并声明插件列表,就可以运行 mitmdump 调用这个脚本了,在这里,我们将其起名为 krisp.py 并在8081端口上监听API请求。mitmdump和mitmproxy不同,后者是一个TUI程序,带用户界面的,前者纯运行功能,没有界面,可以类比为http协议的tcpdump,这个就很适合长期运行了。

```sh
mitmdump -p 8081 -s krisp.py --mode reverse:http://localhost --set keep_host_header
```

当然啦,我们也不可能每次打开软件都运行这么一行命令,多low啊。我们可以将它封装成一个Docker容器,弄到服务器上去,这样它就可以一直开着不用管啦。

!(https://z.cdn.rabit.pw/imgs/2020/20200706231217.png)

如果读者想部署一个暴露公网、持续运行的激活服务器的话,建议额外在前面挂一个反代,这样log rotate和rate limit都比较方便,最简单的办法是用docker compose在前面套一个traefik,这样连SSL证书也不用自己操心了。不过这一部分配置不在本文的讨论范围之内。

# 最后小小的修改

我们搭了一个假API,得让目标程序用起来。这也很简单。刚才抓包不是抓到两个域名么,`api.krisp.ai` 和 `analytics.krisp.ai` 。直接全局搜索改成自己的域名和mitmdump监听的端口,就成了。

![程序中修改的部分](https://z.cdn.rabit.pw/imgs/2020/20200706232229.png)

# 结语

这篇文章又是一个懒人的教程,两个工具,不到100行代码,实现了一个魔改版REST API,通过比较简单的方式破解了一个应用。有人会说,你这一个C#的程序又没混淆又没壳,这么简单的逆向这不是逗乐么?可再想想,基于抓包的思路哇,即使目标应用是个黑盒,猜API参数的含义还是要比猜寄存器的值简单吧。最重要的,还是搭建Web API的思路和 mitmproxy 框架的用法,这些也许会是其它场景能够用得上的知识宝藏。


## 参考资料

1.金坷垃 | 鬼畜全明星 Wiki | Fandom https://guichu.fandom.com/wiki/%E9%87%91%E5%9D%B7%E5%9E%83
1.Noise Cancelling App https://krisp.ai/

1.0xd4d/dnSpy: .NET debugger and assembly editor https://github.com/0xd4d/dnSpy
1.攻击场景示例:DNS 伪造。 https://github.com/mitmproxy/mitmproxy/blob/ed68e0a1ba/examples/contrib/dns_spoofing.py
1.攻击场景示例:TLS passthrough防止目标主机发现CA异常。 https://github.com/mitmproxy/mitmproxy/blob/ed68e0a1ba/examples/contrib/tls_passthrough.py
1.   插件示例:重定向截获的请求。 https://github.com/mitmproxy/mitmproxy/blob/ed68e0a1ba/examples/addons/http-redirect-requests.py
1.插件示例:伪造返回数据,令远端服务器收不到任何消息。 https://github.com/mitmproxy/mitmproxy/blob/ed68e0a1ba/examples/addons/http-reply-from-proxy.py
1.在 mitmproxy 的 venv 中运行 `pydoc mitmproxy.http` (应用层相关API,如 `http.HTTPFlow` 和 `http.HTTPRequest`)和 `pydoc mitmproxy.connection` (传输层相关API,获取源IP、源端口等信息)获取API文档。
1.Modes of operation - mitmproxy https://docs.mitmproxy.org/stable/concepts-modes/


原文首发于 [本人博客](https://blog.rabit.pw/2020/mitmproxy-mock-api) 与个人公众号: rabyte

lyan 发表于 2020-8-6 10:13

慵懒丶L先森 发表于 2020-8-6 00:20
好奇安卓APK能不能也通过类似的原理或者方法解决某些需要联网才能使用的情况

理论上可以的,但实际 大部分app都会加密请求参数以及返回数据,要修改请求 只能破解加密算法,这个难度很大,

虚无空幻 发表于 2020-8-7 15:06

ttimasdf 发表于 2020-8-7 13:41
你这个信息量有点少……我搜了一下大概是个去广告的软件,光看描述的话似乎只能做到代{过}{滤}理、block ...

阿呆瞄本身就是后台去广告,他自身必须常驻后台,也必须开机自启,也必须支持https,而且还支持注入指定程序,也支持注入64位.
至于修改返回数据,就是写规则麻烦点。
至于拦截发送请求,以前我写过插件,自己写个dll就可以实现拦截修改了。(我记得拦截的内容是整个发送数据包和数据包的长度)

ttimasdf 发表于 2020-8-7 13:41

虚无空幻 发表于 2020-8-5 23:25
我就三个字“阿呆喵”全解决了。

你这个信息量有点少……我搜了一下大概是个去广告的软件,光看描述的话似乎只能做到代{过}{滤}理、block部分网络请求。但注意本文中的需求,我们还需要修改请求,而且只修改请求中的response body,HTTP头包括cookie都是需要透传的,否则软件就直接断网用不了了,而且这软件看起来也不适合后台常驻的样子,似乎只能满足部分需求的样子

虚无空幻 发表于 2020-8-5 23:25

我就三个字“阿呆喵”全解决了。

拉玛西亚 发表于 2020-8-5 23:28

碰到混淆和加壳的咋办

大白痴先生 发表于 2020-8-5 23:31

楼主为啥不直接把请求的语句nop掉,自己创建对象,按自己的要求给属性赋值,这样应该更简单吧?

围笑 发表于 2020-8-5 23:50

感谢,来看看喽

smallchop 发表于 2020-8-6 00:07

感谢楼主分享

慵懒丶L先森 发表于 2020-8-6 00:20

好奇安卓APK能不能也通过类似的原理或者方法解决某些需要联网才能使用的情况

xixicoco 发表于 2020-8-6 00:46

好的,牛逼就完事了

甘蔗 发表于 2020-8-6 01:24

好的,牛逼就完事了

netCheney 发表于 2020-8-6 01:47

谢谢分享,非常感谢
页: [1] 2 3 4
查看完整版本: 使用 mitmproxy 快速搭建破解软件网络验证API——以某音频处理软件为例