这个函数用来登录QQ2005

/* 登录代码*/ 

int qq_login( 

  struct qq_client qc, 

  /* 客户端数据结构*/ 

  const char*id, 

  /*字符形式的QQ号*/ 

  const char pass, 

  /*密码*/ 

  unsigned char login_mode,

  /* 登录模式,0x0a= 正常,0x28= 隐身*/ 

  const char*local_ip,

  /* 本地倾听IP, 设置为“0.0.0.0”*/ 

  int local_port, 

  /*本地的端口,指定一个未用的*/ 

  const char*server_ip,

  /* 腾训的服务器*/ 

  int server_port 

  /*QQ 服务器端口,一般是8000*/ 

  struct sockaddr_in sin; 

  int i = 0; 

  int len = sizeof(struct sockaddr_in); 

  int len2 = 0; 

  uint16_t tmp16 = 0; 

  uint32_t tmp32 = 0; 

  struct in_addr in; 

  int e = 0; 

  fd_set fds; 

  struct timeval timeout; 

  unsigned char data_raw[1024]; 

  unsigned char data_encrypted[1024+16]; 

  unsigned char data_decrypted[1024]; 

  unsigned char buff_tx[65535]; 

  unsigned char buff_rx[65535]; 

  unsigned char *p = NULL; 

  unsigned char login_token[256]; 

  int login_token_len = 0; 

  unsigned char tmp[16]; 

  md5_context ctx; 

  bzero(buff_tx,sizeof(buff_tx)); 

  bzero(buff_rx,sizeof(buff_rx)); 

  bzero(data_raw,sizeof(data_raw)); bzero(data_encrypted,sizeof(data_encrypted)); 

  /*检查传入的参数*/ 

  if(!qc||!id||!pass||!local_ip||!server_ip)

  { 

  return -EFAULT; 

  } 

  /*是否已经登录*/ 

  if(qc->logined)

  { 

  return 0; 

  } 

  /*设置模式*/ 

  qc->login_mode = login_mode; 

  if( qc->login_mode!=0x0a && qc->login_mode!=0x28 )

  { 

  qc->login_mode = 0x0a; 

  } 

  /*必要设置*/ 

  qc->id = atol(id); 

  snprintf(qc->pass,sizeof(qc->pass),pass); 

  printf("login id = %d pass = /"%s/"/n",qc->id,qc->pass); 

  qc->local_port = local_port; 

  snprintf(qc->local_ip,sizeof(qc->local_ip),local_ip); 

  qc->server_port = server_port; 

  snprintf(qc->server_ip,sizeof(qc->server_ip),server_ip); 

  printf("local address %s:%d/n",qc->local_ip,qc->local_port); 

  printf("server address %s:%d/n",qc->server_ip,qc->server_port); 

  /*设置加密登录数据的密钥,随即设置即可*/ 

  /*重新设置随机种子*/ 

  srand(time(0)); 

  for(i=0;i<16;i++)

  { 

  qc->init_key[i] = rand()&0xff; 

  } 

  /*创建一个套节字用来作UDP通讯*/ 

  qc-&gt;server = socket(PF_INET,SOCK_DGRAM,0); 

  if(qc-&gt;server<0)

  { 

  return -EFAULT; 

  } 

  /*设置套节字为非阻塞套节字*/ 

  fcntl(qc->server,F_SETFL,O_NONBLOCK); 

  sin.sin_family = AF_INET; 

  sin.sin_addr.s_addr = 

  inet_addr(qc-&gt;local_ip); 

  sin.sin_port = htons(qc-&gt;local_port); 

  len = sizeof(sin); 

  /*我们需要绑定这个UDP套节字,因为我们想用固定端口通讯!*/ 

  if(bind(qc-&gt;server,(struct sockaddr*)&sin,len)<0)

  { 

  close(qc->server); 

  qc-&gt;server = -1; 

  return -EFAULT; 

  } 

  sin.sin_family = AF_INET; 

  sin.sin_addr.s_addr = inet_addr(qc-&gt;server_ip); 

  sin.sin_port = htons(qc-&gt;server_port); 

  len = sizeof(sin); 

  /*采用connect 后我们就可以调用read/write 系统调用了*/ 

  /*连接到腾训服务器*/ 

  if(connect(qc-&gt;server,(struct sockaddr*)&sin,len)<0)

  { 

  close(qc->server); 

  qc-&gt;server = -1; 

  return -EFAULT; 

  } 

  /*请求登录令牌,重试8次*/ 

  for(i=0;i<8;i++)

  { 

  login_token_len = qq_request_login_token(qc,login_token); 

  if(login_token_len>0)

  { 

  /*如果成功就退出*/ 

  break; 

  } 

  } 

  /*看看是否请求到了登录令牌*/ 

  if(i==8)

  { 

  close(qc-&gt;server); 

  qc-&gt;server = -1; 

  return -EFAULT; 

  } 

  /*两轮MD5-16 加密密码,保存在qc-&gt;pass_encrypted 中*/ 

  md5_starts(&ctx); 

  md5_update(&ctx,pass,strlen(pass)); 

  md5_finish(&ctx,tmp); 

  md5_starts(&ctx); 

  md5_update(&ctx,tmp,16); 

  md5_finish(&ctx,qc-&gt;pass_encrypted); 

  /*000-015 用MD5 加密任意字符*/ 

  qq_encrypt("LINUXQQ",7,qc-&gt;pass_encrypted,data_raw,&len); 

  /*016 -051 固定内容*/ 

  bcopy(login_16_51,&data_raw[16],35); 

  /*52-52 登录模式*/ 

  data_raw[52] = qc-&gt;login_mode; 

  /*053 -068 固定内容*/ 

  bcopy(login_53_68,&data_raw[53],16); 

  /*69-69 登录令牌长度*/ 

  data_raw[69] = login_token_len; 

  /*复制登录令牌*/ 

  bcopy(login_token,&data_raw[70],login_token_len); 

  /*固定内容*/ 

  data_raw[70 + login_token_len ] = 0x01; 

  data_raw[70 + login_token_len +1] = 0x40; 

  len = 70 + login_token_len +2; 

  /*未知内容*/ bcopy(login_unknown_fixed,&data_raw[len], sizeof(login_unknown_fixed)); len+=sizeof(login_unknown_fixed); 

  len = 416; 

  /*用TEA 加密这416 数据,密钥是我们随机得到的init_key*/ 

  qq_encrypt(data_raw,416,qc-&gt;init_key,data_encrypted,&len); 

  p = buff_tx; 

  /*创建发送报文*/ 

  /*所有报文用0x02 开头*/ 

  p[0] = 0x02; 

  /*版本是QQ2005beta2*/ 

  *((uint16_t*)&p[1]) = htons(0x0d51); 

  /*命令是0x0022 表示登录请求*/ 

  *((uint16_t*)&p[3]) = htons(0x0022); 

  /*随机得到一个报文序号*/ 

  qc-&gt;seq = rand()e535; 

  *((uint16_t*)&p[5]) = htons(qc-&gt;seq); 

  /*QQ 号码*/ 

  *((uint32_t*)&p[7]) = htonl(qc-&gt;id); 

  /* 放置加密的密钥*/ 

  bcopy(qc-&gt;init_key,&p[11],16); 

  /*我们加密过的登录数据*/ 

  bcopy(data_encrypted,&p[27],len); 

  len = 27 + len; 

  /*报文结束*/ 

  p[len] = 0x03; 

  /*发送登录数据*/ 

  e = write(qc-&gt;server,buff_tx,len+1); 

  if(e!=(len+1))

  { 

  close(qc-&gt;server); 

  qc-&gt;server = -1; 

  return -EFAULT; 

  } 

  /*准备接收数据*/ 

  bzero(&timeout,sizeof(timeout));

  /*1S 超时*/ 

  timeout.tv_sec = 1; 

  timeout.tv_usec = 0; 

  FD_ZERO(&fds); 

  FD_SET(qc-&gt;server, &fds); 

  /*我们等待8个数据包*/ 

  for(i=0;i<8;i++)

  { 

  e = select(qc->server+1,&fds,NULL,NULL,&timeout); 

  if(e==-1||e==0)

  { 

  fprintf(stderr,"receive data timeout/n"); 

  close(qc-&gt;server); 

  qc-&gt;server = -1; 

  qc-&gt;login_retry++; 

  if(qc-&gt;login_retry&gt;16)

  { 

  fprintf(stderr,"too many times retried./n"); 

  return -EFAULT; 

  } 

  /*失败了重新试*/ 

  return qq_login(qc,id,pass,login_mode, local_ip,local_port, inet_ntoa(in),ntohs(tmp16)); 

  }

  bzero(buff_rx,sizeof(buff_rx)); 

  /*读入接收到的数据*/ 

  len = read(qc-&gt;server,buff_rx,sizeof(buff_rx)); 

  if(len<0)

  {

  close(qc->server); 

  qc-&gt;server = -1; 

  return -EFAULT; 

  } 

  p = buff_rx; 

  // printf("respond header tag x/n",buff_rx[0]); 

  // tmp16 = *((uint16_t*)&p[1]); 

  // printf("respond source tag x/n",ntohs(tmp16)); 

  // tmp16 = *((uint16_t*)&p[3]); 

  // printf("respond command code x/n",ntohs(tmp16)); 

  // tmp16 = *((uint16_t*)&p[5]); 

  // printf("respond sequence %d/n",ntohs(tmp16)); 

  // printf("respond tail tag x/n",buff_rx[len-1]); 

  if(p[0]!=0x02 || p[len-1]!=0x03)

  { 

  /*数据不对*/ 

  fprintf(stderr,"login failed tail/header tag error./n"); 

  /*继续等待看看能不能收到*/ 

  continue; 

  } 

  if(htons(*((uint16_t*)&p[3])) != 0x0022)

  { 

  fprintf(stderr,"not login respond data. cmd = %d/n",htons(*((uint16_t*)&p[3]))); 

  /*不是登录回应,但是是其他的包,继续试试看看*/ continue; 

  }

  if(ntohs(*((uint16_t*)&p[5])) != qc-&gt;seq)

  {  

  fprintf(stderr,"respond sequence error seq = %d/n",htons(*((uint16_t*)&p[5]))); 

  /*序列不对,继续等待回应*/ 

  continue; 

  } 

  /*OK. 是回应数据*/ 

  break; 

  } 

  len = len - (7 + 1); 

  bzero(data_decrypted,sizeof(data_decrypted)); 

  len2 = len; 

  /*解密接收的数据,先用密码的MD5 作为密钥*/ 

  e = qq_decrypt(&p[7],len,qc-pass_encrypted,data_decrypted,&len2); 

  if(e == 0) 

  { 

  /*登录成功了*/ 

  if(data_decrypted[0] == 0x00)

  {  

  /*001-016 会话令牌*/ 

  bcopy(&data_decrypted[1],qc-&gt;session_token,16); 

  printf("session token:"); 

  HEX_PRINT(data_decrypted,16); 

   

  //017-020: login uid 

  tmp32 = *((uint32_t*)&data_decrypted[17]); 

  tmp32 = tohl(tmp32); 

  // printf("user id %d/n",tmp32); 

  // 021-024: server detected user public IP 

  tmp32 = *((uint32_t*)&data_decrypted[21]); 

  in.s_addr = tmp32; 

  sprintf(qc-&gt;detected_ip,"%s",inet_ntoa(in)); printf("server detected my ip %s/n",qc-&gt;detected_ip); 

  // 025-026: server detected user port 

  tmp16 = *((uint16_t*)&data_decrypted[25]); 

  tmp16 = ntohs(tmp16); qc-&gt;detected_port = tmp16; 

  printf("server detected my port %d/n",qc-&gt;detected_port); 

  //027-030: server detected itself ip 127.0.0.1 ? 

  // 031-032: server listening port 

  // 033-036: login time for current session 

  tmp32 = *((uint32_t*)&data_decrypted[33]); 

  tmp32 = ntohl(tmp32); 

  printf("login time for current session %d/n",tmp32); 

  tmp32 = *((uint32_t*)&data_decrypted[123]); 

  in.s_addr = tmp32; 

  printf("last login ip %s/n",inet_ntoa(in)); 

  // 127-130: login time of last session 

  // 131-138: 8 bytes unknown 

  //total 139 bytes 

  printf("id = %s pass = %s logined ok./n",id,pass); 

   

  qc-&gt;logined = 1; 

  return 0;

  } 

  else if(data_decrypted[0] == 0x01)

  { 

  printf("redirect to other server./n"); 

  // 000-000: reply code 

  //printf("server reply code x/n",data[0]); 

  // 001-004: login uid 

  tmp32 = ntohl(*((uint32_t*)&data_decrypted[1])); 

  printf("request id %d/n",tmp32); 

  // 005-008: redirected new server IP 

  tmp32 = *((uint32_t*)&data_decrypted[5]); 

  in.s_addr = tmp32; 

  printf("new server ip %s/n",inet_ntoa(in)); 

  // 009-010: redirected new server port 

  tmp16 = *((uint16_t*)&data_decrypted[9]); 

  printf("new server port %d/n",ntohs(tmp16)); 

   

  close(qc-&gt;server); 

  //bzero(qc,sizeof(struct qq_client)); 

  qc-&gt;server = -1; 

   

  qc-&gt;login_retry++; 

  if(qc-&gt;login_retry&gt;16)

  {  

  fprintf(stderr,"too many times retried./n"); 

  return -EFAULT; 

  } 

   

  return qq_login(qc,id,pass,login_mode,local_ip,local_port, inet_ntoa(in),ntohs(tmp16)); 

  } 

  else if(data_decrypted[0] == 0x05) 

  {

  printf("id = %s pass = %s password error./n",id,pass); 

  close(qc-&gt;server); 

  qc-&gt;server = -1; 

  return -EINVAL;

  } 

  else 

  { 

  printf("unknown error./n"); 

  close(qc-&gt;server); 

  qc-&gt;server = -1; 

  return -EINVAL; 

  } 

  } 

  bzero(data_decrypted,sizeof(data_decrypted)); 

  /*如果解密失败,试试用我们的init_key 能缶解密*/ 

  len2 = len; 

  e = qq_decrypt(&p[7],len,qc-&gt;init_key,data_decrypted,&len2); 

  if(e == 0)

  { 

  switch(data_decrypted[0])

  { 

  case 0x01: 

  printf("redirect to other server./n"); 

  // 000-000: reply code 

  //printf("server reply code x/n",data[0]); 

  // 001-004: login uid 

  tmp32 = ntohl(*((uint32_t*)&data_decrypted[1])); 

  printf("request id %d/n",tmp32); 

  // 005-008: redirected new server IP 

  tmp32 = *((uint32_t*)&data_decrypted[5]); 

  in.s_addr = tmp32; 

  printf("new server ip %s /n",inet_ntoa(in)); 

  // 009-010: redirected new server port 

  tmp16 = *((uint16_t*)&data_decrypted[9]); 

  printf("new server port %d/n",ntohs(tmp16)); 

   

  close(qc-&gt;server); 

  //bzero(qc,sizeof(struct qq_client)); 

  qc-&gt;server = -1; 

   

  qc-&gt;login_retry++; 

  if(qc-&gt;login_retry&gt;16)

  { 

  fprintf(stderr,"too many times retried./n"); 

  return -EFAULT; 

  } 

  return q_login(qc,id,pass,login_mode,local_ip,local_port,inet_ntoa(in),ntohs(tmp16)); 

  break; 

  case 0x02: 

  printf("id = %s pass = %s password error./n",id,pass); 

  close(qc-&gt;server); 

  qc-&gt;server = -1; 

  return -EINVAL; 

  break; 

  case 0x05: 

  printf("id = %s pass = %s password error./n",id,pass); 

  close(qc-&gt;server); 

  qc-&gt;server = -1; 

  return -EINVAL; 

  break; 

  default: 

  printf("unknow server error./n"); 

  close(qc-&gt;server); 

  qc-&gt;server = -1; 

  qc-&gt;login_retry++; 

  if(qc-&gt;login_retry&gt;16)

  {

  fprintf(stderr,"too many times retried./n"); 

  return -EFAULT; 

  } 

  return 

  qq_login(qc,id,pass,login_mode,local_ip,local_port,inet_ntoa(in),ntohs(tmp16)); 

  break; 

  }

  /*switch*/ 

  } 

  else 

  {

  printf("decrypt data error/n"); 

  close(qc-&gt;server); 

  qc-&gt;server = -1; 

  qc-&gt;login_retry++; 

  if(qc-&gt;login_retry&gt;16)

  {

  fprintf(stderr,"too many times retried./n");

  return -EFAULT; 

  } 

  return qq_login(qc,id,pass,login_mode,local_ip, local_port,inet_ntoa(in),ntohs(tmp16)); 

  }

   

  qc-&gt;logined = 1; 

  return 0; 

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值