笑看风云

记录生活中的启迪与感动
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

OICQ协议分析类

Posted on 2010-01-13 14:36  清晨的风  阅读(8106)  评论(1编辑  收藏  举报

头文件:

/**********************************************************************************************
*						OICQ协议分析类
*	version:		1.0
*	author:			yuzaobo
*	description:	OICQ分析是通过端口+特征码来实现的, 目前版本限定TCP连接Server端口为80或443,
					UDP连接Server端口为8000。 每一个OICQ协议包均以0x02开头, 以0x03结尾。

					由于OICQ协议包采用加密方式在网络上传输, 因此对OICQ的分析基于二进制码。
					当OICQ命令为0x00 0xdd(数据包第四位、第五位), 数据方向为LAN2WAN时, 
					Client向Server发送帐号信息, 帐号由四个字节记录, 分别为数据包的8-11位
					由此可以记录登录帐号。

							
				 ----------------------------------------------
				|	    key                   value            |
				|		 |                      |              |
				|	local_accont            session_key        |
				|   packet_number          local_account       |
                 ----------------------------------------------
***********************************************************************************************/

#ifndef _OICQ_ANALYSIS_H
#define _OICQ_ANALYSIS_H

#include <map>
#include "activeuser.h"
#include "netheaders.h"
#include "Crypter.h"
#include "decode.h"
#include "CDataBase.h"

#define OICQ_PASSWD_ERR 0x99		//??? This should lead to policy performing.

//#define MSG_ANSI		// message saved as ascii, or comment it to use utf8 

//define message type
#define TYPE_LOGIN		0
#define TYPE_FRIEND		1
#define TYPE_GROUP		2

using namespace std;

typedef struct tagSessionKey
{
	BYTE skey[16];
}SessionKey, *pSessionKey;

typedef struct tagOicqPasswd
{
	BYTE passwd[16];
}OicqPasswd, *pOicqPasswd;

typedef struct tagOicqInfo
{
	char  type;		// message type: login: 0, friend: 1 and group: 2
	ULONG local_account;
	ULONG remote_account;
	//char  direct;
	char  content[20000];
} SOicqInfo, *PSOicqInfo;

class COicqAnalysis
{
public:
	COicqAnalysis();
	~COicqAnalysis();
	int Run(const PIPHeader ip_header, const PTCPHeader tcp_header, const char* data, const int direct, \
		Single_User_App_Details* suad, PSOicqInfo oicq_info);
	int Run(const PIPHeader ip_header, const PUDPHeader udp_header, const char* data, const int direct, \
		Single_User_App_Details* suad, PSOicqInfo oicq_info);

protected:
	int _Run(const PIPHeader ip_header, const char* data, USHORT length, const int direct, Single_User_App_Details* suad, PSOicqInfo oicq_info);

	map<ULONG, SessionKey> m_mapSessionKey;  //STL map structure, store QQ session key info
	map<unsigned __int64, ULONG>m_mapLocalAccount;		//store local account info
	map<ULONG, OicqPasswd>m_mapPassword;
	map<ULONG, ULONG>m_mapGroupNumber;				//group interior number -- group exterior number pair
};

#endif

 

CPP文件:

#include "OicqAnalysis.h"

COicqAnalysis::COicqAnalysis()
{
	CDataBase* pDb = new CDataBase();

	unsigned int count = 0;
	while(!pDb->ExecuteSql("select * from nboicqaccount"))
	{
		if (++count > 5)  // If it still failed after 5 times, exit
		{
			MessageBoxA(NULL, "ExecuteSql Error while get oicq account info!", "Error", MB_ICONERROR | MB_OK);
			exit(1);
		}
		pDb->ExecuteSql("select * from nboicqaccount");		
	}

	MYSQL_RES* res = pDb->StoreResult();
	//int num_rows = (int)mysql_num_rows(res);
	MYSQL_ROW row;

	while (row = mysql_fetch_row(res))
	{
		OicqPasswd oicq_passwd;
		char temp[3] = "";
		for (int i = 0; i < 16; i++)
		{
			char* endptr;	
			temp[0] = row[1][2*i];
			temp[1] = row[1][2*i+1];
			oicq_passwd.passwd[i] = (BYTE)strtol(temp, &endptr, 16);			
		}
		m_mapPassword.insert(map<ULONG, OicqPasswd>::value_type(atoi(row[0]), oicq_passwd));
	}
	pDb->FreeResult(res);

	delete pDb;
}

COicqAnalysis::~COicqAnalysis()
{

}

//TCP
int COicqAnalysis::Run(const PIPHeader ip_header, const PTCPHeader tcp_header, const char *data, const int direct, Single_User_App_Details *suad, PSOicqInfo oicq_info)
{
	if (suad->app_confirm != 2)
	{
		suad->analysis_count++;
	}

	if (suad->remote_port != 443/* && suad->remote_port != 80*/)
	{
		return USER_ERROR;
	}

	USHORT length = ip_header->length - ip_header->headerLength - tcp_header->headerLength;
	USHORT real_length = ((UCHAR)data[0]<<8) + (UCHAR)data[1];
	if (length != real_length)
	{
		return USER_ERROR;
	}
	const char* real_data = data + 2;
	return _Run(ip_header, real_data, length-2, direct, suad, oicq_info);
}

//UDP
int COicqAnalysis::Run(const PIPHeader ip_header, const PUDPHeader udp_header, const char* data, const int direct, Single_User_App_Details* suad, PSOicqInfo oicq_info)
{
	if (suad->app_confirm != 2)
	{
		suad->analysis_count++;
	}

	if (suad->remote_port != 8000)
	{
		return USER_ERROR;
	}

	suad->direct = APPDIR_EXTERIOR;		//UDP, could not determine application direction by SYN and ACK

	USHORT length = udp_header->length - 8;
	return _Run(ip_header, data, length, direct, suad, oicq_info);

}

int COicqAnalysis::_Run(const PIPHeader ip_header, const char* data, USHORT length, const int direct, Single_User_App_Details* suad, PSOicqInfo oicq_info)
{
	////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//
	// 0x00dd(密码验证), 0x00e5(获取登录信息), 0x30(登录验证, 获取用于整个会话过程的session key)
	// 0x00cd(发送消息), 0x0017(接收消息)
	//
	////////////////////////////////////////////////////////////////////////////////////////////////////////////

	if (data[0] == 0x02 && data[length-1] == 0x03)		// OICQ packet start with 0x02 and end with 0x03
	{
		suad->app_id = APPID_QQ;
		suad->app_confirm = 2;

		/*************************************************************************************************
		*					Password verify(0x00dd): Client --> Server
		*************************************************************************************************/
		if (direct == LAN2WAN && data[3] == 0x00 && (UCHAR)data[4] == 0xdd)		
		{
			//get random key
			BYTE random_key[16] =  "";
			memcpy(random_key, &(data[11]), 16);

			//decrypt
			BYTE pbRet[2000] = "";
			CCrypter crypter;
			int out_len = crypter.Decrypt((BYTE*)data, 27, length-28, random_key, pbRet, 2000);
			if (out_len <= 0)
			{
				OutputDebugStringA("0xdd(LAN2WAN) decrypt packet error.\n");	
				return USER_CONFIRM;
			}

			//get encrypted 0x78 bytes key
			BYTE encrypted_key[120] = "";
			memcpy(encrypted_key, &(pbRet[82]), 120);

			//get password(2 times md5)
			BYTE passwd_key[16] = "";
			ULONG local_account = (((UCHAR)data[7])<<24) + (((UCHAR)data[8])<<16) + (((UCHAR)data[9])<<8) + (UCHAR)data[10];
			map<ULONG, OicqPasswd>::iterator iter_passwd = m_mapPassword.find(local_account);
			if (iter_passwd == m_mapPassword.end())	
			{
				OutputDebugStringA("0xdd(LAN2WAN) Registered QQ password is incorrect!\n");	
				oicq_info->local_account = local_account;		//alarm, should know QQ number
				return OICQ_PASSWD_ERR;
			}
			else	
			{
				memcpy(passwd_key, iter_passwd->second.passwd, 16);			
			}

			//decrypt 0x78 bytes key
			BYTE decrypted_key[104] = "";
			out_len = crypter.Decrypt(encrypted_key, 0, 120, passwd_key, decrypted_key, 104);
			if (out_len <= 0)
			{
				OutputDebugStringA("0xdd(LAN2WAN) decrypt 0x78 bytes key error\n");	
				oicq_info->local_account = local_account;		//alarm, should know QQ number, ip can get from ip_header.source
				return OICQ_PASSWD_ERR;  //used for QQ alarm, thus disconnect user from internet
			}

			//and store it in m_mapSessionKey
			SessionKey session_key;
			memcpy(session_key.skey, &(decrypted_key[88]), 16);
			map<ULONG, SessionKey>::iterator iter1 = m_mapSessionKey.find(local_account);
			if (iter1 == m_mapSessionKey.end())	
			{
				m_mapSessionKey.insert(map<ULONG, SessionKey>::value_type(local_account, session_key));

			}
			else	//login, modify session key
			{
				iter1->second = session_key;			
			}

			//store ip+port--local_account pair in m_mapLocalAccount
			unsigned __int64 ip_port = (((unsigned __int64)(ip_header->source))<<32) + suad->local_port;	// key = local_ip<<32 + local_ip; so that it's unique
			map<unsigned __int64, ULONG>::iterator iter = m_mapLocalAccount.find(ip_port);
			if (iter == m_mapLocalAccount.end())	
			{
				m_mapLocalAccount.insert(map<unsigned __int64, ULONG>::value_type(ip_port, local_account));
			}
			else	
			{
				iter->second = local_account;
				//OutputDebugStringA("00xdd(LAN2WAN) packet_number conflict.\n");	
			}

			return USER_CONFIRM;
		}

		/*************************************************************************************************
		*				Password verify(0x00dd): Server --> Client
		*************************************************************************************************/
		if (direct == WAN2LAN && data[3] == 0x00 && (UCHAR)data[4] == 0xdd)	
		{
			//find local_account
			ULONG local_account = 0;
			unsigned __int64 ip_port = (((unsigned __int64)(ip_header->destination))<<32) + suad->local_port;
			map<unsigned __int64, ULONG>::iterator iter1 = m_mapLocalAccount.find(ip_port);
			if (iter1 == m_mapLocalAccount.end())	
			{
				OutputDebugStringA("0xdd(WAN2LAN): find local_account error.\n");	
				return USER_CONFIRM;
			}
			else	
			{
				local_account = iter1->second;
			}

			//find key
			SessionKey session_key;
			map<ULONG, SessionKey>::iterator iter2 = m_mapSessionKey.find(local_account);
			if (iter2 == m_mapSessionKey.end())	
			{
				OutputDebugStringA("0xdd(WAN2LAN): Read key by local_account error.\n");	
				return USER_CONFIRM;
			}
			else	
			{
				session_key = iter2->second;
			}

			//decrypt packet
			BYTE pbRet[2000] = "";
			CCrypter crypter;
			int out_len = crypter.Decrypt((BYTE*)data, 7, length-8, session_key.skey, pbRet, 2000);
			if (out_len <= 0)
			{
				OutputDebugStringA("0xdd(WAN2LAN): decrypt error.\n");	
				return USER_CONFIRM;		
			}

			//get key for next 0x00e5 and insert into m_mapSessionKey
			memcpy(session_key.skey, &(pbRet[out_len-18]), 16);
			map<ULONG, SessionKey>::iterator iter = m_mapSessionKey.find(local_account);
			if (iter == m_mapSessionKey.end())	
			{
				OutputDebugStringA("0xdd(WAN2LAN): Modify key by local_account error.\n");	
				return USER_CONFIRM;
			}
			else	//login, modify session key
			{
				iter->second = session_key;	
				return USER_CONFIRM;
			}			
		}


		/*************************************************************************************************
		*				Login(0x00e5): Server --> Client
		*************************************************************************************************/
		if (direct == WAN2LAN && data[3] == 0x00 && (UCHAR)data[4] == 0xe5)
		{
			//find local_account
			ULONG local_account = 0;
			unsigned __int64 ip_port = (((unsigned __int64)(ip_header->destination))<<32) + suad->local_port;
			map<unsigned __int64, ULONG>::iterator iter1 = m_mapLocalAccount.find(ip_port);
			if (iter1 == m_mapLocalAccount.end())	
			{
				OutputDebugStringA("0xe5(WAN2LAN): find local_account error.\n");	
				return USER_CONFIRM;
			}
			else	
			{
				local_account = iter1->second;
			}

			//find key
			SessionKey session_key;
			map<ULONG, SessionKey>::iterator iter2 = m_mapSessionKey.find(local_account);
			if (iter2 == m_mapSessionKey.end())	
			{
				OutputDebugStringA("0xe5(WAN2LAN): Read key by local_account error.\n");	
				return USER_CONFIRM;
			}
			else	
			{
				session_key = iter2->second;
			}

			//decrypt packet
			BYTE pbRet[2000] = "";
			CCrypter crypter;
			int out_len = crypter.Decrypt((BYTE*)data, 7, length-8, session_key.skey, pbRet, 2000);
			if (out_len <= 0)
			{
				OutputDebugStringA("0xe5(WAN2LAN): decrypt error.\n");	
				return USER_CONFIRM;		
			}

			//get key for next 0x00e5 and insert into m_mapSessionKey
			memcpy(session_key.skey, &(pbRet[4]), 16);
			map<ULONG, SessionKey>::iterator iter = m_mapSessionKey.find(local_account);
			if (iter == m_mapSessionKey.end())	
			{
				OutputDebugStringA("0xe5(WAN2LAN): Modify key by local_account error.\n");	
				return USER_CONFIRM;
			}
			else	//modify session key
			{
				iter->second = session_key;	
				return USER_CONFIRM;
			}		
		}


		/*************************************************************************************************
		*				Login Verify(0x0030): Server --> Client
		*************************************************************************************************/
		if (direct == WAN2LAN && data[3] == 0x00 && (UCHAR)data[4] == 0x30)
		{
			//find local_account
			ULONG local_account = 0;
			unsigned __int64 ip_port = (((unsigned __int64)(ip_header->destination))<<32) + suad->local_port;
			map<unsigned __int64, ULONG>::iterator iter1 = m_mapLocalAccount.find(ip_port);
			if (iter1 == m_mapLocalAccount.end())	
			{
				OutputDebugStringA("0x30(WAN2LAN): find local_account error.\n");	
				return USER_CONFIRM;
			}
			else	
			{
				local_account = iter1->second;
			}

			//find key
			SessionKey session_key;
			map<ULONG, SessionKey>::iterator iter2 = m_mapSessionKey.find(local_account);
			if (iter2 == m_mapSessionKey.end())	
			{
				OutputDebugStringA("0x30(WAN2LAN): Read key by local_account error.\n");	
				return USER_CONFIRM;
			}
			else	
			{
				session_key = iter2->second;
			}

			//decrypt packet
			BYTE pbRet[2000] = "";
			CCrypter crypter;
			int out_len = crypter.Decrypt((BYTE*)data, 7, length-8, session_key.skey, pbRet, 2000);
			if (out_len <= 0)
			{
				OutputDebugStringA("0x30(WAN2LAN): decrypt error.\n");	
				return USER_CONFIRM;		
			}

			//get session key and insert into m_mapSessionKey
			memcpy(session_key.skey, &(pbRet[1]), 16);
			map<ULONG, SessionKey>::iterator iter = m_mapSessionKey.find(local_account);
			if (iter == m_mapSessionKey.end())	
			{
				OutputDebugStringA("0x30(WAN2LAN): Modify key by local_account error.\n");	
				//return USER_CONFIRM;
			}
			else	//modify session key
			{
				iter->second = session_key;	
				//return USER_CONFIRM;
			}	

			//login succeed, return login info
			oicq_info->type = TYPE_LOGIN;
			oicq_info->local_account = local_account;
			oicq_info->remote_account = 0;
			strcpy(oicq_info->content, "<Login>");
			return USER_SUCCEED;			
		}

		/*************************************************************************************************
		*				Send Friend Message(0x00cd): Client --> Server
		*************************************************************************************************/
		if (direct == LAN2WAN && data[3] == 0x00 && (UCHAR)data[4] == 0xcd)
		{
			ULONG local_account = (((UCHAR)data[7])<<24) + (((UCHAR)data[8])<<16) + (((UCHAR)data[9])<<8) + (UCHAR)data[10];
			map<ULONG, SessionKey>::iterator iter = m_mapSessionKey.find(local_account);
			if (iter == m_mapSessionKey.end())
			{
				OutputDebugStringA("0xcd(LAN2WAN): Read key by local_account error.\n");	
				return USER_CONFIRM;
			}
			else	//modify session key
			{
				BYTE pbRet[2000] = "";
				CCrypter crypter;
				int out_len = crypter.Decrypt((BYTE*)data, 11, length-12, iter->second.skey, pbRet, 2000);
				if (out_len <= 0)
				{
					OutputDebugStringA("0xcd(LAN2WAN): decrypt error.\n");	
					return USER_CONFIRM;		// should change another code, used for QQ alarm
				}


				if (out_len < 96)
				{
					OutputDebugStringA("0xcd(LAN2WAN):Invalid receive message.\n");	
					return USER_CONFIRM;
				}


				if (pbRet[11] == 0x08)		//send to QQ2008
				{
					USHORT fontname_len = (pbRet[89]<<8) +pbRet[90];

					if (pbRet[46] == 0x00 && pbRet[47] == 0x0b && pbRet[93+fontname_len] == 0x01)
					{
						ULONG remote_account = (pbRet[4]<<24) + (pbRet[5]<<16) + (pbRet[6]<<8) + pbRet[7];

						oicq_info->type = TYPE_FRIEND;
						oicq_info->local_account = local_account;
						oicq_info->remote_account = remote_account;

						USHORT message_len = (pbRet[97+fontname_len]<<8) +pbRet[98+fontname_len];
						memset(oicq_info->content, 0, 2000);
						memcpy(oicq_info->content, &(pbRet[99+fontname_len]), message_len);
#ifdef MSG_ANSI
						CDeCode decoder;
						decoder.Utf8ToGB2312(oicq_info->content, 1999);

						decoder.ReplaceChar("'", "''", oicq_info->content, 1999);
						decoder.ReplaceChar("\\", "\\\\", oicq_info->content, 1999);

#endif
						return USER_SUCCEED;
					}
					else
					{
						return USER_CONFIRM;
					}
					
				}
				else					//send to QQ2009
				{
					USHORT len = (pbRet[20]<<8) +pbRet[21];
					USHORT fontname_len = (pbRet[91+len]<<8) +pbRet[92+len];

					if (pbRet[48+len] == 0x00 && pbRet[49+len] == 0x0b && pbRet[95+len+fontname_len] == 0x01)
					{
						ULONG remote_account = (pbRet[4]<<24) + (pbRet[5]<<16) + (pbRet[6]<<8) + pbRet[7];

						oicq_info->type = TYPE_FRIEND;
						oicq_info->local_account = local_account;
						oicq_info->remote_account = remote_account;

						USHORT message_len = (pbRet[99+len+fontname_len]<<8) +pbRet[100+len+fontname_len];
						memset(oicq_info->content, 0, 2000);
						memcpy(oicq_info->content, &(pbRet[101+len+fontname_len]), message_len);
#ifdef MSG_ANSI
						CDeCode decoder;
						decoder.Utf8ToGB2312(oicq_info->content, 1999);

						decoder.ReplaceChar("'", "''", oicq_info->content, 1999);
						decoder.ReplaceChar("\\", "\\\\", oicq_info->content, 1999);

#endif
						return USER_SUCCEED;
					}
					else
					{
						return USER_CONFIRM;
					}

				}				
				
			}
		}

		/*************************************************************************************************
		*				Send Group Message(0x0002): Client --> Server
		*************************************************************************************************/
		if (direct == LAN2WAN && data[3] == 0x00 && (UCHAR)data[4] == 0x02)
		{
			ULONG local_account = (((UCHAR)data[7])<<24) + (((UCHAR)data[8])<<16) + (((UCHAR)data[9])<<8) + (UCHAR)data[10];
			map<ULONG, SessionKey>::iterator iter = m_mapSessionKey.find(local_account);
			if (iter == m_mapSessionKey.end())
			{
				OutputDebugStringA("0x02(LAN2WAN): Read key by local_account error.\n");	
				return USER_CONFIRM;

			}
			else	//modify session key
			{
				BYTE pbRet[2000] = "";
				CCrypter crypter;
				int out_len = crypter.Decrypt((BYTE*)data, 11, length-12, iter->second.skey, pbRet, 2000);
				if (out_len <= 0)
				{
					OutputDebugStringA("0x02(LAN2WAN): Could not decrypted, this may be caused by the error password.\n");	
					return USER_CONFIRM;		// should change another code, used for QQ alarm
				}

				if (pbRet[0] == 0x72 && pbRet[1] == 0x00)		//sub command: group info
				{
					ULONG interior_number = (pbRet[2]<<24) + (pbRet[3]<<16) + (pbRet[4]<<8) + pbRet[5];		//interior group number
					ULONG exterior_number = (pbRet[6]<<24) + (pbRet[7]<<16) + (pbRet[8]<<8) + pbRet[9];

					map<ULONG, ULONG>::iterator iter = m_mapGroupNumber.find(interior_number);
					if (iter == m_mapGroupNumber.end())
					{
						m_mapGroupNumber.insert(map<ULONG, ULONG>::value_type(interior_number, exterior_number));
						return USER_CONFIRM;
					}
					else
					{
						return USER_CONFIRM;
					}
				}
				else if (pbRet[0] == 0x2a)	//sub command: send message
				{
					if (pbRet[1] == 0x00)		//result is 00
					{
						return USER_CONFIRM;
					}

					ULONG interior_number = (pbRet[1]<<24) + (pbRet[2]<<16) + (pbRet[3]<<8) + pbRet[4];		//interior group number
					ULONG exterior_number = 0;
					map<ULONG, ULONG>::iterator iter = m_mapGroupNumber.find(interior_number);
					if (iter == m_mapGroupNumber.end())
					{
						OutputDebugStringA("0x02(LAN2WAN): Find exterior number by interior number error.\n");	
						//return USER_CONFIRM;
					}
					else
					{
						exterior_number = iter->second;
					}

					oicq_info->type = TYPE_GROUP;
					oicq_info->local_account = local_account;
					oicq_info->remote_account = exterior_number;

					USHORT font_len = (pbRet[41]<<8) + pbRet[42];
					USHORT content_len = (pbRet[49+font_len]<<8) + pbRet[50+font_len];

					memset(oicq_info->content, 0, 2000);
					memcpy(oicq_info->content, &(pbRet[51+font_len]), content_len);
#ifdef MSG_ANSI
					CDeCode decoder;
					decoder.Utf8ToGB2312(oicq_info->content, 1999);

					decoder.ReplaceChar("'", "''", oicq_info->content, 1999);
					decoder.ReplaceChar("\\", "\\\\", oicq_info->content, 1999);

#endif
					return USER_SUCCEED;
				}
				else
				{
					return USER_CONFIRM;	//other group command
				}
			}
		}


		/*************************************************************************************************
		*		Receive Message QQ2008 or Receive Group Message QQ2009(0x0017): Server --> Client
		*************************************************************************************************/
		if (direct == WAN2LAN && data[3] == 0x00 && (UCHAR)data[4] == 0x17)
		{
			//find local_account
			ULONG local_account = 0;
			unsigned __int64 ip_port = (((unsigned __int64)(ip_header->destination))<<32) + suad->local_port;
			map<unsigned __int64, ULONG>::iterator iter1 = m_mapLocalAccount.find(ip_port);
			if (iter1 == m_mapLocalAccount.end())	
			{
				OutputDebugStringA("0x17(WAN2LAN): find local_account error.\n");	
				return USER_CONFIRM;
			}
			else	
			{
				local_account = iter1->second;
			}

			//find key
			SessionKey session_key;
			map<ULONG, SessionKey>::iterator iter2 = m_mapSessionKey.find(local_account);
			if (iter2 == m_mapSessionKey.end())	
			{
				OutputDebugStringA("0x17(WAN2LAN): Read key by local_account error.\n");	
				return USER_CONFIRM;
			}
			else	
			{
				session_key = iter2->second;
			}

			//decrypt packet
			BYTE pbRet[2000] = "";
			CCrypter crypter;
			int out_len = crypter.Decrypt((BYTE*)data, 7, length-8, session_key.skey, pbRet, 2000);
			if (out_len <= 0)
			{
				OutputDebugStringA("0x17(WAN2LAN): decrypt error.\n");	
				return USER_CONFIRM;		
			}

			/*---------------------	Analysis	module-------------------------------*/
			//if (pbRet[24] == 0x12 && pbRet[25] == 0x21)	//by version
			if (pbRet[18] == 0x00 && pbRet[19] == 0x09)	//QQ2008: Friend Message, by im type
			{
				if (out_len < 52)
				{
					OutputDebugStringA("0x17(WAN2LAN):Invalid receive message.\n");	
					return USER_CONFIRM;
				}

				USHORT len = (pbRet[22]<<8) +pbRet[23];
				if (pbRet[50+len] == 0x00 && pbRet[51+len] == 0x0b && pbRet[69+len] != 0x14)	//not face
				{
					ULONG remote_account = (pbRet[0]<<24) + (pbRet[1]<<16) + (pbRet[2]<<8) + pbRet[3];

					oicq_info->type = TYPE_FRIEND;
					oicq_info->local_account = local_account;
					oicq_info->remote_account = remote_account;
					memset(oicq_info->content, 0, 2000);				
					strcpy(oicq_info->content, (const char*)&(pbRet[69+len]));
#ifndef MSG_ANSI
					CDeCode decoder;
					decoder.GB2312ToUtf8(oicq_info->content, 1999);

					decoder.ReplaceChar("'", "''", oicq_info->content, 1999);
					decoder.ReplaceChar("\\", "\\\\", oicq_info->content, 1999);

#endif
					return USER_SUCCEED;
				}
				else
				{
					return USER_CONFIRM;
				}
			}
			else if (pbRet[18] == 0x00 && pbRet[19] == 0x2b)	//QQ2008 Group message
			{
				if (out_len < 58)
				{
					OutputDebugStringA("0x17(WAN2LAN):Invalid receive group message.\n");	
					return USER_CONFIRM;
				}

				USHORT len = (pbRet[22]<<8) +pbRet[23];
				ULONG remote_account = (pbRet[29]<<24) + (pbRet[30]<<16) + (pbRet[31]<<8) + pbRet[32];

				if (remote_account == local_account)		//filter
				{
					return USER_CONFIRM;
				}

				oicq_info->type = TYPE_GROUP;
				oicq_info->local_account = local_account;
				oicq_info->remote_account = remote_account;
				memset(oicq_info->content, 0, 2000);				
				strcpy(oicq_info->content, (const char*)&(pbRet[57+len]));
#ifndef MSG_ANSI
				CDeCode decoder;
				decoder.GB2312ToUtf8(oicq_info->content, 1999);

				decoder.ReplaceChar("'", "''", oicq_info->content, 1999);
				decoder.ReplaceChar("\\", "\\\\", oicq_info->content, 1999);

#endif
				return USER_SUCCEED;
			}
			else if (pbRet[18] == 0x00 && pbRet[19] == 0x52)	//QQ2009: Group Message
			{
				if (out_len < 92)
				{
					OutputDebugStringA("0x17(WAN2LAN):Invalid receive group message.\n");	
					return USER_CONFIRM;
				}
				else
				{
					ULONG interior_number = (pbRet[0]<<24) + (pbRet[1]<<16) + (pbRet[2]<<8) + pbRet[3];		//interior group number
					ULONG exterior_number = (pbRet[24]<<24) + (pbRet[25]<<16) + (pbRet[26]<<8) + pbRet[27];

					//maintain interior-exterior map
					map<ULONG, ULONG>::iterator iter = m_mapGroupNumber.find(interior_number);
					if (iter == m_mapGroupNumber.end())
					{
						m_mapGroupNumber.insert(map<ULONG, ULONG>::value_type(interior_number, exterior_number));
					}

					oicq_info->type = TYPE_GROUP;
					//ULONG local_account = (pbRet[4]<<24) + (pbRet[5]<<16) + (pbRet[6]<<8) + pbRet[7];
					ULONG remote_account = (pbRet[29]<<24) + (pbRet[30]<<16) + (pbRet[31]<<8) + pbRet[32];
					//oicq_info->local_account = exterior_number;		
					oicq_info->local_account = local_account;
					//oicq_info->remote_account = exterior_number;
					oicq_info->remote_account = remote_account;

					if (remote_account == local_account)		//filter
					{
						return USER_CONFIRM;
					}

					USHORT font_len = (pbRet[81]<<8) + pbRet[82];
					USHORT content_len = (pbRet[89+font_len]<<8) + pbRet[90+font_len];
					memset(oicq_info->content, 0, 2000);
					memcpy(oicq_info->content, &(pbRet[91+font_len]), content_len);
#ifdef MSG_ANSI
					CDeCode decoder;
					decoder.Utf8ToGB2312(oicq_info->content, 1999);

					decoder.ReplaceChar("'", "''", oicq_info->content, 1999);
					decoder.ReplaceChar("\\", "\\\\", oicq_info->content, 1999);

#endif
					return USER_SUCCEED;
				}
			}
			else
			{
				return USER_CONFIRM;
			}
		}

		/*************************************************************************************************
		*				Receive Friend Message QQ2009(0x00ce): Server --> Client
		*************************************************************************************************/
		if (direct == WAN2LAN && data[3] == 0x00 && (UCHAR)data[4] == 0xce)
		{
			//find local_account
			ULONG local_account = 0;
			unsigned __int64 ip_port = (((unsigned __int64)(ip_header->destination))<<32) + suad->local_port;
			map<unsigned __int64, ULONG>::iterator iter1 = m_mapLocalAccount.find(ip_port);
			if (iter1 == m_mapLocalAccount.end())	
			{
				OutputDebugStringA("0x17(WAN2LAN): find local_account error.\n");	
				return USER_CONFIRM;
			}
			else	
			{
				local_account = iter1->second;
			}

			//find key
			SessionKey session_key;
			map<ULONG, SessionKey>::iterator iter2 = m_mapSessionKey.find(local_account);
			if (iter2 == m_mapSessionKey.end())	
			{
				OutputDebugStringA("0x17(WAN2LAN): Read key by local_account error.\n");	
				return USER_CONFIRM;
			}
			else	
			{
				session_key = iter2->second;
			}

			//decrypt packet
			BYTE pbRet[2000] = "";
			CCrypter crypter;
			int out_len = crypter.Decrypt((BYTE*)data, 7, length-8, session_key.skey, pbRet, 2000);
			if (out_len <= 0)
			{
				OutputDebugStringA("0x17(WAN2LAN): decrypt error.\n");	
				return USER_CONFIRM;		
			}

			if (out_len < 98)
			{
				OutputDebugStringA("0x17(WAN2LAN):Invalid receive message.\n");	
				return USER_CONFIRM;
			}

			USHORT len = (pbRet[22]<<8) +pbRet[23];
			USHORT fontname_len = (pbRet[93+len]<<8) +pbRet[94+len];
			if (pbRet[18] == 0x00 && pbRet[19] == 0xa6 && pbRet[50+len] == 0x00 && pbRet[51+len] == 0x0b && pbRet[97+len+fontname_len] == 0x01)
			{
				ULONG remote_account = (pbRet[0]<<24) + (pbRet[1]<<16) + (pbRet[2]<<8) + pbRet[3];

				oicq_info->type = TYPE_FRIEND;
				oicq_info->local_account = local_account;
				oicq_info->remote_account = remote_account;
				memset(oicq_info->content, 0, 2000);
				ULONG message_len = (pbRet[101+len+fontname_len]<<8) +pbRet[102+len+fontname_len];
				memcpy(oicq_info->content, &(pbRet[103+len+fontname_len]), message_len);
#ifdef MSG_ANSI
				CDeCode decoder;
				decoder.Utf8ToGB2312(oicq_info->content, 1999);

				decoder.ReplaceChar("'", "''", oicq_info->content, 1999);
				decoder.ReplaceChar("\\", "\\\\", oicq_info->content, 1999);

#endif
				return USER_SUCCEED;
			}
			else
			{
				return USER_CONFIRM;
			}
		}

		//================================================================================================================================

		return USER_CONFIRM;		// OICQ protocol

	}
	else
	{
		return USER_ERROR;		// not OICQ protocol
	}
}