QQLite的奇葩破解之法——DNS+WebServer
本帖最后由 maozhenyu 于 2017-5-12 14:08 编辑最近回国了,有点空。于是再来一篇,是我之前完成的QQLite的破解。
不得不说这个东西确实做的很好,几乎实现了QQ协议所有的功能:包括不限于正常的发消息、图片、表情,甚至包括发红包等等。
程序的基本原理是QQLite 调用QQLite.Framework.dll。核心在QQLite.Framework.dll。因此我的目标是自己制作一个“外壳”,调用QQLite.Framework.dll 来实现我的功能。
如果你准备直接引用dll,使用命名空间,………………,然后…… 哦,你想得美!
说明里面有联网认证
首先用抓包软件抓取这个QQLite在验证时的网络请求。发现这样的请求:POST http://auth.qqlite.cn/Authorize.php
请求返回了一堆16进制小写字母,怀疑是被AES加密。那么我们着手分析程序。
可惜这东西是用最新的DNGuard HVM 的企业版加的壳,破解起来十分的有难度——至少我没这个时间和耐心去破。好在52pojie有提供过DNGuard HVM Unpacker for 3.71 trial 的版本,虽然无法完全脱开QQLite的壳,但是可以去掉很大一部分。通过搜索,我们发现了在这里。
private readonly string[] ABIONX4Oko = new string[] { "http://qqlite.94qing.com/Authorize.php", "http://auth.94qing.com/Authorize.php", "http://www.qqlite.cn/Authorize.php" };
string str = "无法访问服务器,请确定联网状态";
//注意 为了可读性 我对这里的变量名和方法名进行了一些修改
private bool AuthHosts() //这里读取hosts文件,看看有没有对qqlite域名劫持(常规题,改127.0.0.1然后伪造认证服务器)
{
string path = string.Format(@"{0}\drivers\etc\hosts", Environment.GetFolderPath(Environment.SpecialFolder.System));
if (System.IO.File.Exists(path))
{
StreamReader reader = new StreamReader(path, Encoding.Default);
string str2 = reader.ReadToEnd();
reader.Close();
if (((str2.IndexOf("qqlite", StringComparison.CurrentCultureIgnoreCase) > -1) || (str2.IndexOf("94qing", StringComparison.CurrentCultureIgnoreCase) > -1)) || (str2.IndexOf("authorize", StringComparison.CurrentCultureIgnoreCase) > -1))
{
return false;
}
}
return true;
}
public void MainAuth(){
if (!this.AuthHosts())
{
if (this.DtaOyWwSSY() == null)
{
this.WIxO7NaA7k(new AuthorizeJson());
}
this.DtaOyWwSSY().result = false;
this.DtaOyWwSSY().message = str;
//如果hosts验证不通过,Boom!
}
else if (Utility.Md5((License.AgentQQ + License.SoftWareQQ) + License.SoftWareName + License.SoftWareAuthor) != License.LdffLjAU3r()) //这里是读取License检验程序防止篡改的
{
str = "非法篡改";
if (this.DtaOyWwSSY() == null)
{
this.WIxO7NaA7k(new AuthorizeJson());
}
this.DtaOyWwSSY().result = false;
this.DtaOyWwSSY().message = str;
}
else //这里开始向服务器发送请求
{
byte[] buffer = Utility.Md5File(Splash.GetType().Assembly.Location); //先获得当前dll的md5
string str3 = string.Empty;
string str4 = string.Empty;
foreach (byte num2 in buffer)
{
str3 = str3 + num2.ToString("X2");//先获得当前dll的md5
}
foreach (byte num4 in Utility.Md5File(Application.ExecutablePath)) //获得当前宿主exe的md5
{
str4 = str4 + num4.ToString("X2");
}
object[] args = new object[] { str3, License.FrameworkVersion, str4, License.Version, this.Jw5OeqK15o().QQ, (this.Jw5OeqK15o().LoginStatus == LoginStatus.Login) ? "1" : "0", bool_0 ? "1" : "0", License.SoftWareQQ, License.AgentQQ, License.FrameworkType };
string str5 = string.Format("FrameworkMd5={0}&FrameworkVersion={1}&ExeMd5={2}&ExeVersion={3}&RobotQQ={4}&IsLogin={5}&IsReStart={6}&BuildQQ={7}&AgentQQ={8}&FrameworkType={9}", args); //传参
string str6 = QIDOLuDhqR();
if (!string.IsNullOrEmpty(str6))
{
str5 = str5 + "&AuthKey=" + str6;
}
DateTime now = DateTime.Now;
int num5 = now.Minute + 10;
string str7 = num5.ToString(CultureInfo.InvariantCulture);
string str8 = (now.Second + 10).ToString(CultureInfo.InvariantCulture);
string str9 = str7 + str8;
string key = Utility.Md5(string.Concat(new object[] { this.Jw5OeqK15o().QQ, "00-01-6C-06-A6-29", License.FrameworkVersion.Replace(".", ""), str9 }));
object[] objArray3 = new object[] { this.Jw5OeqK15o().QQ, License.FrameworkVersion.Replace(".", ""), "00-01-6C-06-A6-29", str9 };
string str11 = Utility.Md5(string.Concat(objArray3)).Substring(5, 0x10);
str5 = PHP.authcode_en(str5, key);
//这里计算authcode
object[] objArray4 = new object[] { jb9BOoatwNaPbmWQJN.FCsPZSC4Oe(now) / 0x3e8L, License.FrameworkVersion, this.Jw5OeqK15o().QQ, HttpUtility.UrlEncode(str5) };
string str12 = string.Format("Do=CheckAuthorize&AuthVersion=2.3&Time={0}&Version={1}&RobotQQ={2}&Data={3}", objArray4);
HttpHelper helper = new HttpHelper();
HttpItem item = new HttpItem {
URL = this.ABIONX4Oko,
Method = "POST",
UserAgent = "QQLite_Framework_" + License.FrameworkVersion,
Postdata = str12
};
HttpResult html = helper.GetHtml(item);
//发出HTTP请求,并获得返回
if (((html.StatusCode == HttpStatusCode.OK) && !string.IsNullOrEmpty(html.Html)) && (html.Html.Length >= 5))
{
string str13 = MdTXO0IDbFmn8RvdCTm.vdTweSWh6L(html.Html, str11);、、
//这里验证返回值
if (!string.IsNullOrEmpty(str13) && (str13.Length >= 5))
{
string str14 = PHP.authcode_de(str13, key);
if (!string.IsNullOrEmpty(str14) && (str14.Length >= 5))
{
AuthorizeJson json = Json.Deserialize<AuthorizeJson>(str14);
if (json == null)
{
if (this.DtaOyWwSSY() == null)
{
this.WIxO7NaA7k(new AuthorizeJson());
}
this.DtaOyWwSSY().result = false;
this.DtaOyWwSSY().message = str;
}
else
{
this.WIxO7NaA7k(json);
if (this.DtaOyWwSSY().authkey != null)
{
object[] objArray5 = new object[] { this.Jw5OeqK15o().QQ, License.Version.Replace(".", ""), str9, "00-01-6C-06-A6-29" };
if (this.DtaOyWwSSY().authkey == Utility.Md5(string.Concat(objArray5)))
{
if (!string.IsNullOrEmpty(this.DtaOyWwSSY().message))
{
this.DtaOyWwSSY().message = HttpUtility.UrlDecode(this.DtaOyWwSSY().message);
}
this.DtaOyWwSSY().authkey = null;
return;
}
}
if (!string.IsNullOrEmpty(this.DtaOyWwSSY().message))
{
this.DtaOyWwSSY().message = HttpUtility.UrlDecode(this.DtaOyWwSSY().message);
}
}
}
else if (int_0 == 0)
{
this.fE8OvhvTfu(bool_0, int_0 + 1);
}
else
{
if (this.DtaOyWwSSY() == null)
{
this.WIxO7NaA7k(new AuthorizeJson());
}
this.DtaOyWwSSY().result = false;
this.DtaOyWwSSY().message = str;
}
}
else if (int_0 == 0)
{
this.fE8OvhvTfu(bool_0, int_0 + 1);
}
else
{
if (this.DtaOyWwSSY() == null)
{
this.WIxO7NaA7k(new AuthorizeJson());
}
this.DtaOyWwSSY().result = false;
this.DtaOyWwSSY().message = str;
}
}
else if (html.StatusCode != HttpStatusCode.NotFound)
{
this.fE8OvhvTfu(bool_0, int_0 + 1);
}
else
{
if (this.DtaOyWwSSY() == null)
{
this.WIxO7NaA7k(new AuthorizeJson());
}
this.DtaOyWwSSY().result = false;
this.DtaOyWwSSY().message = str;
}
}
这里就是认证的核心代码了,我在上面打了注释。
对于这种问题的通常解决方法是根据代码做一个Emulator,伪造的认证服务器。然后通过hosts把向原本认证服务器的请求转移到伪造的服务器上。
可惜,他做了hosts检查,没法用hosts了。
好啊,你不让我用hosts,我用DNS还不行么?立刻动手本地搭一个DNS服务器,C#处理,代码如下:
//这里我使用了ARSoft.Tools的库(http://arsofttoolsnet.codeplex.com/ )
public MyDNSServer()
{
DnsServer dnsServer = new DnsServer(IPAddress.Parse("0.0.0.0"), 1, 1); //监听本地DNS
dnsServer.QueryReceived += DnsServer_QueryReceived;
dnsServer.Start();//this.ProcessQuery
}
private async System.Threading.Tasks.Task DnsServer_QueryReceived(object sender, QueryReceivedEventArgs e) //Handle 请求
{
DnsMessage query = e.Query as DnsMessage;
if (query == null)
return;
DnsMessage response = query.CreateResponseInstance();
string add = query.Questions.Name.ToString();
// check for valid query
if ((query.Questions.Count == 1)
&& (query.Questions.RecordType == RecordType.A)
)
{
if (add== "auth.qqlite.cn.") //如果是对auth.qqlite.cn域名的DNS请求
{
response.AnswerRecords.Add(new ARecord(query.Questions.Name, 36000, IPAddress.Parse("127.0.0.1"))); //给他返回A记录 127.0.0.1,也就是本机
response.ReturnCode = ReturnCode.NoError;
}
}
// set the response
e.Response = response;
}
修改主用DNS为127.0.0.1,备用DNS为你的上级DNS。
那我同时就用这个程序搭建Emulator了,代码截图:
QQLite请求时,我的Emulator的显示:
QQLite主程序能工作了:
主要问题解决了,是不是呢 你好 ,咨询下 “ QQLite请求返回了一堆16进制小写字母,怀疑是被AES加密”,这里的AES我解密结果这么只有部分,还有个尾巴 消失了
他是用的是aes哪种填充方式加密的呢?
aes_key=f81c0a4a5d6b43b9
密文=6cda8ef7094bbd60a3f39564f3c8c6b750234511d6272f372d5ef4899a21ec1f720c0d0a4cfc725a15d6bc6bd8b255311acc92f68cfecd9925009a0299c2c67195e8436b3020dade5c4a5412bc41183c9c32d3c7f3278ec621fe6440cca9fddac96d6751e15881afb388c3afe8a2f9bb2a3671d822a51d90b8a8f4c7a220b5dc00eb274aa5475266a3d7facddf23cf13a50fde56224a1fd6da3b9e82713a91813e8357610af9d75aa911e9c838e2aac3ccef435ad0dd73f7804a9e3022b8d93c5f71bab666ed7195b7c1ed074c31c714a911211185c2ed91950e420fc834fed74cf59e7240c0bcffea83df515b77563049b66c50fe44f2d1274e875be1a4f2cca0f7b79ba0d04cb872fe5dbc161bc663d66106222602f12b2534dc5554d316b8a0f1594da6ecd1de5a5ff2ceb36826693e5f48684995f155cc111b632e2f67caae052ee7be5782fc068f6bd882fec0a9551c7c884d2582ec16b731533ebf66485f2bcb0eeef2d72277a9165843fd344d4ae4fa56f2027ba4846f9a089b2cbc762ae463baa9ea2542627e94783e4ea08ec11313a8f74c1b4ea3adcfa6ef76dec70af9817135d0c28dd163cf130e34161c23396ff8eeb1aa0b2f8676e77019d14a9ca0cdc8e38c0f4cb9d54471d7a5b8b29e9551004f798e1943ab9c1628fcf66ab4c427ea1246791480a748736f576a902983214bb44eebf824534c5ecd766d0fd739566ace91cd077152bc9ce387b3eb72a7eb0cb804926122d31b1a03bf38119ad4e3c91db1ed1b2f17a7c0847ab98f4879a3c931e510027dfae4ff6fb5afd6ccb97b926d085877a25fd7985dd769bf5fbc5e34d6a218f2a68c217301fdb5ecec6c62439e91034dea45f1c0628988401b47992eaa70a4216aabb6c019503ef404a62208c13f3587c3d7c7f3164fc52e
我aes解密的结果是 2a34BQPe5ORETeurQeeeL/Sftw7POvvRpZnxYyYrwdWHb3bLUcliYAlMUoF8b5oi++9QCAV6mUxFAbnDuAFGbUSSFT4MFNCIZQSPLsTP/pJMlU+H4FNV+GT0k9Yxvn7NKHDqGxUZon0lEQGTVOSHSaQAXUwe6XSs/02uI1lD34+YCtpUapKJE8jyIj7rovh7r6OER0/N0Yjj+2D9EkeZs3jlE5Bbmg+t3mYvQJzj8ocZKj4c47Nq+gKpQDH/Lm5JXFV9Bmm5LIJMTEFg0FXVSyDtGRpz6dDs5u35Bhxwy2Rxd3R4wWaFcU2niiJynGCdytLQIdCWuebicCud7f0NXsngYEH7JqHvfxF+kgHX3hy03YHw0JZIxouas/YbzXO0Fe4kYRa6uBxBVpieurqvzu/+UJilW+EaVyE3W8km2zUpVoWuKQrTybVcPTXsou2+O7YoeL2WLMt7w/DVU3Q6vk/FHVATVKNw75uyDPPDoyGYDQMDwZHWhPBqtVzkFgTMpMGeuoJ8m91XvO6aVv5hMkUQiME9hhjcsDZ1HjGaZTz5GnXashZ6wEWKilg+MTqigdgYSNCiQHPhdtTmNd7B//mFVfWOLTO72A7yJoCiZJOHCn5Y
正确的结果是 2a34BQPe5ORETeurQeeeL/Sftw7POvvRpZnxYyYrwdWHb3bLUcliYAlMUoF8b5oi++9QCAV6mUxFAbnDuAFGbUSSFT4MFNCIZQSPLsTP/pJMlU+H4FNV+GT0k9Yxvn7NKHDqGxUZon0lEQGTVOSHSaQAXUwe6XSs/02uI1lD34+YCtpUapKJE8jyIj7rovh7r6OER0/N0Yjj+2D9EkeZs3jlE5Bbmg+t3mYvQJzj8ocZKj4c47Nq+gKpQDH/Lm5JXFV9Bmm5LIJMTEFg0FXVSyDtGRpz6dDs5u35Bhxwy2Rxd3R4wWaFcU2niiJynGCdytLQIdCWuebicCud7f0NXsngYEH7JqHvfxF+kgHX3hy03YHw0JZIxouas/YbzXO0Fe4kYRa6uBxBVpieurqvzu/+UJilW+EaVyE3W8km2zUpVoWuKQrTybVcPTXsou2+O7YoeL2WLMt7w/DVU3Q6vk/FHVATVKNw75uyDPPDoyGYDQMDwZHWhPBqtVzkFgTMpMGeuoJ8m91XvO6aVv5hMkUQiME9hhjcsDZ1HjGaZTz5GnXashZ6wEWKilg+MTqigdgYSNCiQHPhdtTmNd7B//mFVfWOLTO72A7yJoCiZJOHCn5YmombKZ+FdYg8evHMcQ/AAUQQ
多了mombKZ+FdYg8evHMcQ/AAUQQ这尾巴
就这搞不定了。谢谢 沙发沙发。{:301_997:} 求破解版{:301_997:} 大佬厉害! 贴代码啊。老大。学习一下 厉害了哥, 感谢分享 厉害!支持你!! 厉害厉害,这个思路可以啊 完全看不明白!还是支持楼主分享