QQ的HTTP接口PHP探究

1、研究说明
  Tencent在tqq.tencent.com的8000有一个使用HTTP的QQ接口,通过这个接口,可以进行一些基本的操作,如:登陆、登出、改变登陆状态(上线、忙碌、离线、隐身)、添加删除好友、查看好友信息、发送验证信息(接受被加为好友、申请加对方为好友、拒绝被加为好友)、收发用户消息、系统信息。
  目前我研究的是1.1版本的HTTP QQ协议,研究是微程在的成果上进行的,不敢说有什么超越,只不过更为详细和准确。
  2、接口说明:
  接口位置:tqq.tencent.com:8000
  通信协议:HTTP
  数据传输方法:POST
  HTTP请求格式:
  POST HTTP/1.1
  Host: tqq.tencent.com:8000
  Content-Type: text/plain; charset=UTF-8
  Content-length: 长度
  Connection: close
  数据
  其中长度为 数据 的长度,数据的格式:
  VER=1.1&CMD=命令&SEQ=标记&UIN=QQ号&....
  以上4个参数是每个请求都必有的。其中,VER表示协议的版本,目前为1.1,据说1.2已经出来了,这个乱写的话,服务器返回NULL;CMD为操作的指令,有Login、List、Query_Stat、GetInfo、AddToList、Ack_AddToList、DelFromList、Change_Stat、GetMsgEx、CLTMSG、Logout;SEQ为当前请求的标记,防止重复发送,可以用当前时间,也可以用随机数;UIN是当前执行操作的QQ号。不过不同的CMD还需要不同的参数,下面我就公布我的研究成果。
  3、研究方法:
  我对目前网上的资料不够满意,就自己写程序,发送多条相同CMD不同参数的请求,根据服务器的返回,来做判断。感兴趣的朋友可以参考一下,此处可以跳过。
  下面我公布我探测的代码(PHP):
  $uin = "QQ号";
  $pwd = md5("QQ密码");
  //登陆测试
  $poststring[] = "VER=1.1&CMD=Login&SEQ=".rand(1000,9000)."&UIN=".$uin."&PS=".$pwd."&M5=1&LC=9326B87B234E7235";
  //注意:登陆测试不能同时进行,必须等到服务器认为QQ断开了,才能够测试,不然结果不可信
  /*******
  $poststring[] = "VER=1.1&CMD=Login&SEQ=".rand(1000,9000)."&UIN=".$uin."&PS=".$pwd."&M5=0&LC=9326B87B234E7235";
  $poststring[] = "VER=1.1&CMD=Login&SEQ=".rand(1000,9000)."&UIN=".$uin."&PS=".$pwd."&M5=1&LC=9326B87B234E7235";
  $poststring[] = "VER=1.1&CMD=Login&SEQ=".rand(1000,9000)."&UIN=".$uin."&PS=".$pwd."&M5=2&LC=9326B87B234E7235";
  $poststring[] = "VER=1.1&CMD=Login&SEQ=".rand(1000,9000)."&UIN=".$uin."&PS=".$pwd."&M5=3&LC=9326B87B234E7235";
  $poststring[] = "VER=1.1&CMD=Login&SEQ=".rand(1000,9000)."&UIN=".$uin."&PS=".$pwd."&M6=1&LC=9326B87B234E7235";
  $poststring[] = "VER=1.1&CMD=Login&SEQ=".rand(1000,9000)."&UIN=".$uin."&PS=".$pwd."&M6=1&LC=1223423545756679";
  *******/
  //得到好友列表
  $poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin;
  $poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0";
  $poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=160";
  $poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=0";
  $poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=".rand(1,10);
  $poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=0";
  $poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=160&UN=0";
  $poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=160&UN=0";
  $poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=".rand(1,200)."&UN=0";
  $poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=".rand(1,200)."&UN=0";
  $poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=".rand(1,200)."&UN=0";
  $poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=".rand(1,10);
  $poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=".rand(1,10);
  $poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=".rand(1,10);
  $poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=106814";
  //得到在线列表
  $poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin;
  $poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0";
  $poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=160";
  $poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=0";
  $poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=".rand(1,10);
  $poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=0";
  $poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=160&UN=0";
  $poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=160&UN=0";
  $poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=".rand(1,200)."&UN=0";
  $poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=".rand(1,200)."&UN=0";
  $poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=".rand(1,200)."&UN=0";
  $poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=".rand(1,10);
  $poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=".rand(1,10);
  $poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=".rand(1,10);
  $poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=106814";
  //查看好友信息
  $poststring[] = "VER=1.1&CMD=GetInfo&SEQ=".rand(1000,9000)."&UIN=".$uin."&LV=0&UN=106814";
  $poststring[] = "VER=1.1&CMD=GetInfo&SEQ=".rand(1000,9000)."&UIN=".$uin."&LV=1&UN=106814";
  $poststring[] = "VER=1.1&CMD=GetInfo&SEQ=".rand(1000,9000)."&UIN=".$uin."&LV=2&UN=106814";
  $poststring[] = "VER=1.1&CMD=GetInfo&SEQ=".rand(1000,9000)."&UIN=".$uin."&LV=3&UN=106814";
  $poststring[] = "VER=1.1&CMD=GetInfo&SEQ=".rand(1000,9000)."&UIN=".$uin."&LV=4&UN=106814";
  $poststring[] = "VER=1.1&CMD=GetInfo&SEQ=".rand(1000,9000)."&UIN=".$uin."&LV=5&UN=106814";
  //增加好友
  $poststring[] = "VER=1.1&CMD=AddToList&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814";
  //发送验证
  $poststring[] = "VER=1.1&CMD=Ack_AddToList&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814&CD=0&RS=TEST";
  $poststring[] = "VER=1.1&CMD=Ack_AddToList&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814&CD=1&RS=TEST";
  $poststring[] = "VER=1.1&CMD=Ack_AddToList&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814&CD=2&RS=TEST";
  $poststring[] = "VER=1.1&CMD=Ack_AddToList&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814&CD=3&RS=TEST";
  $poststring[] = "VER=1.1&CMD=Ack_AddToList&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814&CD=4&RS=TEST";
  $poststring[] = "VER=1.1&CMD=Ack_AddToList&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814&CD=5&RS=TEST";
  //删除好友
  $poststring[] = "VER=1.1&CMD=DelFromList&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814";
  //改变状态
  for($i=0;$i<=60;$i=$i+5)
  {
  $poststring[] = "VER=1.1&CMD=Change_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&ST=".$i;
  }
  //获得消息
  $poststring[] = "VER=1.1&CMD=GetMsgEx&SEQ=".rand(1000,9000)."&UIN=".$uin."";
  //发送消息
  $poststring[] = "VER=1.1&CMD=CLTMSG&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814&MG=TEST";
  //登出
  $poststring[] = "VER=1.1&CMD=Logout&SEQ=".rand(1000,9000)."&UIN=".$uin."";
  $file = fopen("p.txt","w");
  foreach($poststring as $k=>$v)
  {
  ss_timing_start();
  $fp = fsockopen("tqq.tencent.com", "8000", $errno, $errstr, $timeout = 10);
  if(!$fp){
  //error tell us
  $content = $k.chr(13).chr(10)."ERROR:$errstr ($errno)";
  }else{
  //send the server request
  fputs($fp, "POST HTTP/1.1 ");
  // fputs($fp, "Host: $host ");
  // fputs($fp, "Content-type: application/x-www-form-urlencoded ");
  fputs($fp, "Content-length: ".strlen($v)." ");
  fputs($fp, "Connection: close ");
  fputs($fp, $v . " ");
  //loop through the response from the server
  $res = "";
  while(!feof($fp)) {
  $res .= fgets($fp, 4096);
  }
  //close fp - we are done with it
  fclose($fp);
  $content = $v.chr(13).chr(10).$res;
  }
  ss_timing_stop();
  $content .= chr(13).chr(10)."Time: ".ss_timing_current().chr(13).chr(10)."--------------------------------------".chr(13).chr(10);
  fputs($file,$content);
  }
  fclose($file);
  ?>
  function ss_timing_start ($name = "default") {
  global $ss_timing_start_times;
  $ss_timing_start_times[$name] = explode(" ", microtime());
  }
  function ss_timing_stop ($name = "default") {
  global $ss_timing_stop_times;
  $ss_timing_stop_times[$name] = explode(" ", microtime());
  }
  function ss_timing_current ($name = "default") {
  global $ss_timing_start_times, $ss_timing_stop_times;
  if (!isset($ss_timing_start_times[$name])) {
  return 0;
  }
  if (!isset($ss_timing_stop_times[$name])) {
  $stop_time = explode(" ", microtime());
  }
  else {
  $stop_time = $ss_timing_stop_times[$name];
  }
  $current = $stop_time[1]-$ss_timing_start_times[$name][1];
  $current += $stop_time[0]-$ss_timing_start_times[$name][0];
  return $current;
  }
  ?>
  4、研究成果:
  (1).登陆
  说明:在你做任何其他操作以前,你必须登陆。只有在登陆以后,你的其他指令才有可能被正确执行(返回RES=0),不然服务器会返回RES=20,不过有个例外,就是logout。当你成功登陆以后,服务器就会根据你的IP*和参数中的UIN来验证身份。一台电脑可以同时登陆多个QQ,互不影响,就是因为有参数UIN。
  *至于我能够确定服务器是通过IP来验证的,是因为服务器不可能通过我的请求获得其他信息了^_^
  提交数据:VER=1.1&CMD=Login&SEQ=标记&UIN=QQ号&PS=QQ密码&M5=1&LC=9326B87B234E7235
  说明:QQ密码是通过md5加密的字符串,在PHP中可以直接用md5()进行加密;
  M5这个参数的作用还不清楚,但最好为1。
  LC这个参数有点神秘,不能有丝毫改动,不然服务器就没有响应(没有响应就是返回NULL)。
  返回:VER=1.1&CMD=LOGIN&SEQ=标记&UIN=QQ号&RES=0&RS=0&HI=60&LI=300(成功)
  VER=1.1&CMD=LOGIN&SEQ=标记&UIN=QQ号&RES=0&RS=1&RA=密码错误(密码错误)
  VER=1.1&CMD=LOGIN&SEQ=标记&UIN=QQ号&RES=5(QQ号非法,如100)
  NULL(UIN为字符、PS为空、LC错误)
  (2).得到好友列表
  提交数据:VER=1.1&CMD=List&SEQ=标记&UIN=QQ号&TN=160&UN=0
  说明:TN、UN还不清楚具体表示什么,但是TN的值会影响返回的结果,有没有UN对结果没有影响
  返回:VER=1.1&CMD=LIST&SEQ=标记&UIN=QQ号&RES=0&FN=9(当TN=0或没有TN参数时,FN表示好友数)
  VER=1.1&CMD=LIST&SEQ=标记&UIN=QQ号&RES=0&FN=1&SN=9&UN=3814526,...,(当TN存在且非0时,FN=1,SN表示好友数,UN为好友列表,用","分割)
  VER=1.1&CMD=LIST&SEQ=标记&UIN=QQ号&RES=20(没有正确登陆)
  NULL(UIN、TN、UN为字符)
  (3).得到在线好友列表
  提交数据:VER=1.1&CMD=Query_Stat&SEQ=标记&UIN=QQ号&TN=50&UN=0
  说明:TN、UN还不清楚具体表示什么,但是TN的值会影响返回的结果,有没有UN对结果没有影响
  返回:VER=1.1&CMD=QUERY_STAT&SEQ=标记&UIN=QQ号&RES=0&FC=0,&FN=1&SN=1&ST=10,&UN=106814,&NK=Hackfan 好,(当TN存在且非0时,FN=1,SN表示在线好友数,FC、ST、UN、NK的值用","分割,分别表示头像、状态、号码、昵称)
  VER=1.1&CMD=QUERY_STAT&SEQ=标记&UIN=QQ号&RES=20(没有正确登陆)
  NULL(UIN、TN、UN为字符)
  说明:FC为QQ头像的的ID,如的头像ID为270,那么其头使用的图片为91.bmp,其算法为ID/3+1;
  ST为QQ用户的状态,10为上线,20为离线(或隐身),30为忙碌;
  特别说明:当参数TN=0或不存在时,服务器返回:
  VER=1.1&CMD=Query_Stat&SEQ=标记&UIN=QQ号
  HTTP/1.1 200 OK
  Server: tencent imserver/1.0.0
  Content-Type: text/plain; charset=UTF-8
  Content-Length: 56
  VER=1.1&CMD=QUERY_STAT&SEQ=标记&UIN=QQ号&RES=0&FN=1
  HTTP/1.1 200 OK
  Server: tencent imserver/1.0.0
  Content-Type: text/plain; charset=UTF-8
  Content-Length: 77
  VER=1.1&CMD=QUERY_STAT&SEQ=标记&UIN=QQ号&RES=0&FC=&FN=1&SN=0&ST=&UN=&NK=
  返回了2次,第一次的结果中,FN为在线好友数,第二次返回的数据基本没用。
  (4).查看好友信息
  提交数据:VER=1.1&CMD=GetInfo&SEQ=标记&UIN=QQ号&LV=查询类型&UN=被查询QQ号码
  说明:LV=0,1为精简查询,LV=2为普通查询,LV>=3为详细查询
  返回:VER=1.1&CMD=GETINFO&SEQ=标记&UIN=QQ号&RES=0&LV=0&UN=106814&NK=Hackfan 好(精简查询)
  VER=1.1&CMD=GETINFO&SEQ=标记&UIN=QQ号&RES=0&AD=地址&AG=19&EM=hackfan@qq.com&FC=0&HP=http://blog.hackfan.net&JB=学生
  &LV=2&PC=邮编&PH=电话&PR=The guy is updating to .NET Frameword......&PV=江苏&RN=胡吉阳&SC=毕业院校&SX=0&UN=106814&NK=Hackfan
  好(普通查询)
  VER=1.1&CMD=GETINFO&SEQ=标记&UIN=QQ号&RES=0&AD=地址&AG=19&BT=2&CO=6&CT=苏州&CV=%01&CY=中华人民共和国
  &EM=hackfan@qq.com&FC=0&HP=http://blog.hackfan.net&ID=-&JB=学生&LV=3&MO=136********&MT=0&MV=&PC=邮编&PH=电话&PR=The guy is
  updating to .NET Frameword......&PV=江苏&RN=胡吉阳&SC=毕业院校&SH=3&SX=0&UN=106814&NK=Hackfan 好(详细查询)
  VER=1.1&CMD=GETINFO&SEQ=标记&UIN=QQ号&RES=20(没有正确登陆)  NULL(UIN、LV、UN为字符)

时间:2009-12-25 | 分类:技术文摘 | 标签: PHP  QQ  HTTP  API 
评论列表
暂无评论
发表评论
昵称
邮箱
内容