这个元旦假期过的比较奇怪,为了向远方的GF表示忠诚。除了1号和同学吃了个大锅饭之外,尽量做到了大门不出二门不迈,躲在闺房守岁:)。
在家闲着也不能干闲着,总得做点事情,想起前段时间一个师兄交代给我的任务:把若干个qq群串在一起。X国的政策一向比较Bt,你说开个群还要限制人数。H大在深圳有几千校友,一个群怎么可能放的下那么多人。好多在tx工作的师兄,明确告诉:我买通tx的人是不大可能了,搞一个传话筒才是解决之道。
事情虽小,但也着实麻烦。你说要去分析QQ具体数据包,万一哪天tx一高兴把数据给改了,或者接口开放了,也够我郁闷一壶的了。那就找个最简单的办法吧,左一顿baidu,又一顿google. “QQ接口”搜出来的结果绝大多数是关于tx qq的http接口,既然大家那么推崇,那我就从Http接口下手,于是把我以前写的AJAX给拆了,在把所谓的qq接口研究成果给鼓捣在一起,然后去webqq(
http://webqq.qq.com)上大抢一遍,那家伙,天昏地暗,相当的&(&(。
代码我是用script写的:
1//异步AJAX会话类
2if(typeof(AjaxSession) == 'undefined')

3var AjaxSession = function()
{
4
5 //创建会话
6 this.CreateSession = function()

7
{

8 if (window.ActiveXObject)
{ // IE

9 try
{
10 return new ActiveXObject('Microsoft.XMLHTTP');

11 }catch(e)
{}
12

13 try
{
14 return new ActiveXObject('Msxml2.XMLHTTP');

15 }catch(ee)
{}

16 }else
{ //Mozilla, Safari
17 var s = new XMLHttpRequest();

18 if (s.readyState == null)
{
19 s.readyState = 1;

20 s.addEventListener("load", function ()
{
21 s.readyState = 4;
22 if (typeof(s.onreadystatechange) == "function")
23 s.onreadystatechange();
24 }, false);
25 }
26
27 return s;
28 }
29
30 return null;
31 }
32
33 //进行请求
34 this.Request = function(url, params, callback)

35
{
36
37 var s = this.CreateSession();
38 if(null == s)
39 alert("对不起,您的浏览器不支持某些特性。");
40 else
41 s.abort();
42
43 var isAsync = typeof(callback) == 'function';
44 var method = !params ? "GET" : "POST";
45
46 if(isAsync) s.onreadystatechange = function()

47
{

48 try
{
49 alert(s.status);
50 if ((4 == s.readyState) && (200 == s.status || 304 == s.status))
51 callback(this.Response(s));
52 else
53 alert("请求错误,错误原因:" + s.statusText);

54 }catch(e)
{}
55 }
56
57 s.open(method, url, isAsync);
58 s.setRequestHeader("Connection","Keep-Alive");
59 s.setRequestHeader("Content-Type","text/html; charset=gb2312");
60 //s.setRequestHeader("Content-Type","text/plain; charset=UTF-8");
61
62 if(method == "POST")

63
{
64 s.setRequestHeader("Content-Length",params.length)
65 s.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
66
67 }
68
69 alert(params);
70 s.send(params);
71
72
73 if(!isAsync)
74 return this.Response(s);
75 }
76
77 //返回应答信息
78 this.Response = function(s)

79
{
80 if(s.status == 200 || 304 == s.status)

81
{
82 if(s.responseXML != null && s.responseXML.xml != null && s.responseXML.xml != '')
83 return s.responseXML;
84 else
85 return s.responseText;
86 }
87 }
88
89 //对字符串进行编码
90 this.UncCoding = function(s)

91
{
92 var output = '';

93 for(var i = 0 ;i< s.length;i++)
{
94 output = output + '%' + s.charCodeAt(i);
95 }
96
97 return output;
98 }
99
100 //获取xml结构

101 this.GetDom = function(s)
{
102 var doc = new ActiveXObject('Microsoft.XMLDOM');
103 doc.async = false;
104 doc.loadXML(s) ;
105 return doc;
106 }
107
108 return this;
109}
1//会话类
2if(typeof(QQSession) == 'undefined')

3var QQSession = function()
{
4 this.UIN = 0;
5 this.Md5PasswordStr = "";
6 this.WebQQUrl = "http://tqq.tencent.com:8000/";
7 this.qs = null;
8 this.isLogin = false;
9 this.SeqIndex = null;
10
11 //用户登陆

12 this.Login = function(uin,passwd,vcode,qs)
{
13 var m5 = new MD5();
14 this.UIN = uin;
15 this.Md5PasswordStr = m5.calcMD5(passwd);
16 var CmdStr = "VER=1.0&CMD=1&SEQ=" + this.GetNewSEQ() + "&UIN=" + uin + "&PS=" + this.Md5PasswordStr + "&STATUS=10&VCODE=" + vcode + "&QQSESSION=" + qs ;
17
18 //if(!this.qs)
19 // this.qs = new AjaxSession();
20 window.frames["proxy"].request(this.WebQQUrl,CmdStr);
21 //this.qs.Request(this.WebQQUrl,CmdStr,this.ShowMsg);
22
23 //document.getElementById("ssl_login").callback = this.ShowMsg;
24 //document.getElementById("ssl_login").src = "https://webqq-proxy.qq.com/webqq/l?"+ CmdStr;
25 }
26
27 //用户信息

28 this.GetInfo = function()
{
29 var CmdStr = "VER=1.0&CMD=10&SEQ=" + this.GetNewSEQ() + "&UIN=" + this.UIN + "&FUIN=" + this.UIN;
30 this.qs.Request(this.WebQQUrl,CmdStr,this.ShowMsg);
31 //window.frames["proxy"].request(this.WebQQUrl,CmdStr);
32 }
33
34 //获取好友列表

35 this.GetList = function()
{
36 var CmdStr = "VER=1.0&CMD=2&SEQ=" + this.GetNewSEQ() + "&UIN=" + this.UIN + "&NPOS=0";
37 this.qs.Request(this.WebQQUrl,CmdStr,this.ShowMsg);
38 }
39
40 //获得新的SEQ

41 this.GetNewSEQ = function()
{

42 if(!this.SeqIndex)
{
43 var d = new Date();
44 this.SeqIndex = d.getTime()
45 }
46
47 this.SeqIndex ++;
48 return this.SeqIndex;
49 }
50

51 this.ShowMsg = function(s)
{
52 alert(s);
53 }
54
55 return this;
56}
我在那用天真的眼神,盼望着能从这个地方掏出点东西来。可偏偏我这个比较倒霉的孩子,碰到了极其复杂的网络问题(家穷人丑,只好用手机上网,速度回到上世纪90年代),掏了半天啥也没弄到,返回的尽是12152错误。
去翻MSDN时,遇到几个头疼的单词(鄙人英语着实差劲,单词量屈指可数),很习惯的就去开金山词霸。等等,金山词霸能把别的窗口的信息给拽出来,为什么我就不能。于是我就抛弃了前面的工作,从QQ对话窗口下手。nndx,偶就不信了,偶还灭不了你。
说干就干,那就先勾吧,.net好象是干不了这事,但是winapi还是能干这活的。win32编程嘛,不就几个消息循环(—(—……*(—
1using System;
2using System.Drawing;
3using System.Runtime.InteropServices;
4
5namespace TQQ

6
{

7 /**//**//**//// <summary>

8 /**//// WinApi调用

9 /**//// </summary>
10 public class WinApi

11
{

12 /**//**//**//// <summary>

13 /**//// 根据鼠标位置获取窗体

14 /**//// </summary>

15 /**//// <param name="lpPoint"></param>

16 /**//// <returns></returns>
17 [DllImport("user32.dll")]
18 public static extern IntPtr WindowFromPoint(Point lpPoint);
19

20 /**//**//**//// <summary>

21 /**//// 获取鼠标位置

22 /**//// </summary>

23 /**//// <param name="lpPoint"></param>

24 /**//// <returns></returns>
25 [DllImport("user32.dll")]
26 public static extern int GetCursorPos(out Point lpPoint);
27

28 /**//**//**//// <summary>

29 /**//// 获取鼠标位置下的窗体

30 /**//// </summary>

31 /**//// <returns></returns>
32 public static IntPtr GetLocalWindow()

33
{
34 Point point;
35 GetCursorPos(out point);
36 return WindowFromPoint(point);
37 }
38

39 /**//**//**//// <summary>

40 /**//// 申请内存空间

41 /**//// </summary>

42 /**//// <param name="hProcess"></param>

43 /**//// <param name="lpAddress"></param>

44 /**//// <param name="dwSize"></param>

45 /**//// <param name="flAllocationType"></param>

46 /**//// <param name="flProtect"></param>

47 /**//// <returns></returns>
48 [ DllImport( "Kernel32.dll" )]
49 public static extern Int32 VirtualAllocEx(IntPtr hProcess,Int32 lpAddress,Int32 dwSize,Int16 flAllocationType,Int16 flProtect);
50

51 /**//**//**//// <summary>

52 /**//// 读取内存空间

53 /**//// </summary>

54 /**//// <param name="hProcess"></param>

55 /**//// <param name="lpBaseAddress"></param>

56 /**//// <param name="lpBuffer"></param>

57 /**//// <param name="nSize"></param>

58 /**//// <param name="lpNumberOfBytesWritten"></param>

59 /**//// <returns></returns>
60 [ DllImport( "Kernel32.dll" )]
61 public static extern int ReadProcessMemory(IntPtr hProcess, Int32 lpBaseAddress,byte[] lpBuffer,long nSize,long lpNumberOfBytesWritten);
62

63 /**//**//**//// <summary>

64 /**//// 写内存空间

65 /**//// </summary>

66 /**//// <param name="hProcess"></param>

67 /**//// <param name="lpBaseAddress"></param>

68 /**//// <param name="lpBuffer"></param>

69 /**//// <param name="nSize"></param>

70 /**//// <param name="lpNumberOfBytesWritten"></param>

71 /**//// <returns></returns>
72 [ DllImport( "Kernel32.dll" )]
73 public static extern int WriteProcessMemory(IntPtr hProcess, Int32 lpBaseAddress,byte[] lpBuffer,long nSize,long lpNumberOfBytesWritten);
74

75 /**//**//**//// <summary>

76 /**//// 根据类/标题查找窗口

77 /**//// </summary>

78 /**//// <param name="lpClassName"></param>

79 /**//// <param name="lpWindowName"></param>

80 /**//// <returns></returns>
81 [DllImport("User32.dll",EntryPoint="FindWindow")]
82 public static extern IntPtr FindWindow(string lpClassName,string lpWindowName);
83

84 /**//**//**//// <summary>

85 /**//// 获取窗口子对象

86 /**//// </summary>

87 /**//// <param name="hwndParent"></param>

88 /**//// <param name="hwndChildAfter"></param>

89 /**//// <param name="lpszClass"></param>

90 /**//// <param name="lpszWindow"></param>

91 /**//// <returns></returns>
92 [DllImport("user32.dll",EntryPoint="FindWindowEx")]
93 public static extern IntPtr FindWindowEx(IntPtr hwndParent,IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
94

95 /**//**//**//// <summary>

96 /**//// 发送windows消息

97 /**//// </summary>

98 /**//// <param name="hWnd"></param>

99 /**//// <param name="Msg"></param>

100 /**//// <param name="wParam"></param>

101 /**//// <param name="lParam"></param>

102 /**//// <returns></returns>
103 [DllImport("User32.dll")]
104 public static extern IntPtr SendMessage(IntPtr hWnd,int Msg,IntPtr wParam,IntPtr lParam);
105

106 /**//**//**//// <summary>

107 /**//// 发送windows消息

108 /**//// </summary>

109 /**//// <param name="hWnd"></param>

110 /**//// <param name="Msg"></param>

111 /**//// <param name="wParam"></param>

112 /**//// <param name="lParam"></param>

113 /**//// <returns></returns>
114 [DllImport("User32.dll",EntryPoint="SendMessage")]
115 public static extern int SendMessage(IntPtr hWnd,int Msg, IntPtr wParam, string lParam);
116

117 /**//**//**//// <summary>

118 /**//// 发送windows消息

119 /**//// </summary>

120 /**//// <param name="hwnd"></param>

121 /**//// <param name="wMsg"></param>

122 /**//// <param name="wParam"></param>

123 /**//// <param name="lParam"></param>

124 /**//// <returns></returns>
125 [DllImport("user32.dll", CharSet = CharSet.Auto)]
126 public static extern int SendMessage( IntPtr hwnd, int wMsg, int wParam,string lParam);
127

128 /**//**//**//// <summary>

129 /**//// 发送windows消息

130 /**//// </summary>

131 /**//// <param name="hwnd"></param>

132 /**//// <param name="wMsg"></param>

133 /**//// <param name="wParam"></param>

134 /**//// <param name="lParam"></param>

135 /**//// <returns></returns>
136 [DllImport("user32.dll", CharSet = CharSet.Auto)]
137 public static extern int SendMessage( IntPtr hwnd, int wMsg, int wParam,int lParam);
138

139 /**//**//**//// <summary>

140 /**//// 发送windows消息

141 /**//// </summary>

142 /**//// <param name="hwnd"></param>

143 /**//// <param name="wMsg"></param>

144 /**//// <param name="wParam"></param>

145 /**//// <param name="lParam"></param>

146 /**//// <returns></returns>
147 [DllImport("user32.dll", CharSet = CharSet.Auto)]
148 public static extern int SendMessage( IntPtr hwnd, int wMsg, int wParam,System.Text.StringBuilder lParam);
149
150 public const int WM_GETTEXT = 0x000D;
151 public const int WM_GETTEXTLENGTH = 0x000E;
152 public const int WM_SETTEXT = 0x000C;
153 public const int WM_CLICK = 0x00F5;
154 public const int WM_CHAR = 0x0102;
155 public const int EM_SETSEL = 0x00B1;
156 public const int EM_REPLACESEL = 0x00C2;
157
158 }
159}
东西到手了,传话筒的工作基本上算是完成了,接下来就让它显示出来:

1 /**//**//**//// <summary>
2 /**//// 时钟事件
3 /**//// </summary>
4 /**//// <param name="sender"></param>
5 /**//// <param name="e"></param>
6 private void tmGetMsg_Tick(object sender, System.EventArgs e)

7
{
8 //群1里的消息
9 string strMsg = QQHooks.GetMsg(txtGroupWinName1.Text);
10 string str = "";
11
12 if(!string.Empty.Equals(strMsg))

13
{
14 ArrayList msgList = ParseMsg.Parse(strMsg);
15
16 foreach(QQMsg msg in msgList)

17
{
18 if("253822559" == msg.Number.ToString())continue;
19
20 if(string.Empty.Equals(msg.Msg.Trim()))
21 str = string.Format("{0}在群1里做了个表情\r\n",msg.Name,msg.Msg);
22 else
23 str = string.Format("{0}在群1里说:\r\n{1}\r\n",msg.Name,msg.Msg);
24
25 lbGroupMsgList1.Items.Add(str);
26 //save msg
27
28 //发送消息
29 QQHooks.SendMsg(txtGroupWinName2.Text,str);
30 QQHooks.SendMsg(txtGroupWinName3.Text,str);
31
32 }
33 }
34 }
这里的txtGroupWinName1是让输入qq窗口标题。以前用asm32做程序的时候就觉的微软够BT的,要是每个窗口编译的时候就给限制个GUID多好—()—*)((—
至此大功算是告成了,完美交差。拿着这东东,改天狠狠的宰我师兄几顿.
(07-01-04)续:
要的人太多了,我还是主动把东西传上来吧,要不然有骗 email地址之嫌
源代码和相关资料下载:/Files/sukyboor/Q.rar