本帖最后由 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的壳,但是可以去掉很大一部分。通过搜索,我们发现了在这里。
[C#] 纯文本查看 复制代码 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 [b]AuthHosts[/b]() //这里读取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.[b]AuthHosts[/b]())
{
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[int_0],
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#处理,代码如下:
[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[0].Name.ToString();
// check for valid query
if ((query.Questions.Count == 1)
&& (query.Questions[0].RecordType == RecordType.A)
)
{
if (add== "auth.qqlite.cn.") //如果是对auth.qqlite.cn域名的DNS请求
{
response.AnswerRecords.Add(new ARecord(query.Questions[0].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主程序能工作了:
主要问题解决了,是不是呢 |