前言
今天本着学习研究的态度,下载了一个滴答清单PC pj版,发布日期大概是19年4月左右。
下载之后登录时却遇到了问题,输完账号密码点击登录,然后就会提示Login_OutDate,大概就是说用户不是VIP了,不能用,请重新登录
由于比较好奇为什么之前能用,现在不能用了,于是逆了一下代码,然后就有了这篇文章,成功的绕过了登录后弹窗的问题,以及无法同步的限制
正文
首先根据关键字定位到弹窗代码
可以从上图看到,程序弹窗之后就会关闭当前窗体,重置界面登录状态,显示登录窗体,那么我们就可以通过return大法很轻松的让程序不退出登录并继续执行
进行修改后,我们继续分析函数关系,找到上级调用,看看还有没有其他检测
可以看到,上面其实也是对当前登录的用户信息进行了一个重置的操作的,所以这边也是要继续return掉的
保险起见继续回溯,看上级调用
从这里可以看出来,程序请求了滴答清单的API接口来获取用户信息,如果检测到用户非法,就执行刚刚我们找到的那一系列函数进行登出操作。
那么做到这一步之后,就解决了PC端登录秒退的问题,用户已经可以正常本地使用原破解版的所有功能了,但目前还不能同步,会提示网络错误
所以我们继续看为什么会走到:response.StatusCode == HttpStatusCode.Unauthorized,能否通过修改判断逻辑实现数据同步
为了方便大家理解,我把上面那张图中所在函数的关键逻辑贴出来
嫌乱的可以跳过这部分代码,我一会还会用文字解释
HttpClient httpClient = ProxyHelper.GetHttpClient();
httpClient.DefaultRequestHeaders.Add("Authorization", "OAuth " + **auth**);
httpClient.DefaultRequestHeaders.Add("User-Agent", "TickTickClient/1.0");
httpClient.DefaultRequestHeaders.Add("x-device", Utils.GetDeviceInfo());
string domain = BaseUrl.GetDomain();
string uriString;
if (!**fulluri**)
{
uriString = domain + **api**;
}
else
{
uriString = **api**;
}
string mode2 = **mode**;
if (!(mode2 == "POST"))
{
if (!(mode2 == "GET"))
{
if (!(mode2 == "PUT"))
{
if (mode2 == "DELETE")
{
response = httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Delete, new Uri(uriString))
{
Content = new StringContent(**content**, Encoding.UTF8, "application/json")
}).Result;
}
}
else
{
response = httpClient.PutAsync(new Uri(uriString), new StringContent(**content**, Encoding.UTF8, "application/json")).Result;
}
}
else
{
response = httpClient.GetAsync(new Uri(uriString)).Result;
}
}
else if (**paramList** == null || **paramList**.Count == 0)
{
response = httpClient.PostAsync(new Uri(uriString), new StringContent(**content**, Encoding.UTF8, "application/json")).Result;
}
else
{
response = httpClient.PostAsync(new Uri(uriString), new FormUrlEncodedContent(**paramList**)).Result;
}
if (response != null && (response.StatusCode == HttpStatusCode.OK | **isNeedErrorReturn**))
{
responseReturn = response.Content.ReadAsStringAsync().Result;
if (App.ProExpiredSyncError)
{
App.ProExpiredSyncError = false;
}
}
else if (response != null && response.StatusCode == HttpStatusCode.InternalServerError)
{
responseReturn = response.Content.ReadAsStringAsync().Result;
if (!string.IsNullOrEmpty(responseReturn))
{
NetWork.HandleApiError(responseReturn);
}
}
else if (response != null && response.StatusCode == HttpStatusCode.Unauthorized)
{
Utils.TokenOutDate();
}
这段代码大概说了个啥呢?其实逻辑很简单,就下面4个步骤:
- 获取当前设备信息
- 获取需要同步的数据(比如新增、删除、修改待办事项这些)
- 将需要同步的数据和当前设备信息发送到滴答清单的远程API接口
- 根据远程API返回判断是否同步成功
总的来说,这些逻辑是通过传参数到服务端进行校验,本地并没有做会员状态检测,直接调用的服务端API接口
但!滴答清单有一个比较特殊的地方,他的手机端数据是可以相互同步的
也就是说,我们或许可以通过修改电脑端发出的请求,使服务端API认为我们是一个手机APP,这样我们就可以绕过检测直接同步了
通过手机APP抓包对比,我发现只需要修改:
httpClient.DefaultRequestHeaders.Add("x-device", Utils.GetDeviceInfo());
为:
httpClient.DefaultRequestHeaders.Add("x-device", “手机端UA”);
即可模拟成手机请求
修改的过程中我发现其实原破解作者也是相同的思路,他修改了Utils里的GetDeviceInfo函数,强制返回一个iPad的UA,按理说是可以同步的(并且前几个月确实能正常使用)
那么我可以做出合理推测,原作者发布破解版本后,被大家广泛使用,最后滴答清单官方团队取得了破解样本,进行分析后,对该UA进行了拉黑处理
也就是说——目前网上流传的那些破解版本没准都是出自一人之手。。所以一个UA封掉之后全部不能用了
总而言之,通过修改UA的值,即可绕过服务端API检测,实现数据同步
后记
回顾了一下整个流程,其实官方应该是拉黑了破解版的UA,所以只要换掉UA,应该就不会出现异常检测了,也就是说不需要去在函数内做return。
博客文章迁移: 2020-01-12 01:23