大家都知道现在有很多申请QQ号的软件,只要填写软件上的验证码,就可以帮你自动申请一个QQ,并保存下来。 它简化了 注册的流程。


通常这类软件有两种,一个编程时用了IE的控件,直接向这些表单填写数据,另一种就是今天要讲的,用封包的方法来实现自动申请QQ。

想一下,我们面临的难题有:
1.验证码的gif图片在哪,如何让程序获取图片,并显示出来。
2.http请求中包的分析和构建。
当然第二个是重点。

先完成第一项任务。
我们打开 www.reg.qq.com ,会有这个http请求

用文字就是:
GET: http://captcha.qq.com/getimage?aid=1007901&0.3621022082160978
Accept: */*
Referer: http://reg.qq.com/
Accept-Language: zh-CN
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; User-agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; http://bsalsa.com) ; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0E; .NET4.0C)
Host: captcha.qq.com
Connection: Keep-Alive
---------------------------------------------
http://captcha.qq.com/getimage?aid=1007901&0.3621022082160978
看这个url,有两个数据,是否是定值呢。
再重新登陆 reg.qq.com,会发现 aid=后面的数字没有变,但第二个数据变化了。
这个很容易理解,因为每次登陆会有不同的验证码嘛。

但第一个数据,仔细看看之前发的包,就会知道 1007901,是GetDay产生的,过一天可能就加1了。呵呵!但今天无论怎么换验证码,都不会改变的。

OK。 gif图片已经获取。
 

代码:
char  szAccept[]   = "Accept: */gif";
    char  szReferer[]   = "Referer:  http://reg.qq.com/\r\n\r\n";
  CString strBuffer   = _T("");
  CString  strRecv     = _T("");

  HINTERNET  hSession;   
  HINTERNET   hConnect;   
  HINTERNET   hRequest;   
  BOOL    bReturn   = FALSE;
  CString str=_T("/getimage?aid=1007901&0.0022421072022002497");
  // 之前使用Socket,现在使用Wininet相关API建立链接
  hSession = InternetOpen("InetAll",INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0);
  hConnect = InternetConnect(hSession,"captcha.qq.com",INTERNET_DEFAULT_HTTP_PORT,NULL,NULL,INTERNET_SERVICE_HTTP,0,1);
  hRequest = HttpOpenRequest(hConnect,"GET",str,NULL,"http://reg.qq.com/",(PCSTR*)szAccept,INTERNET_FLAG_RELOAD,1);

  // 提交数据表单
  
  bReturn = HttpSendRequest(hRequest,NULL,-1,NULL,0);
  if(!bReturn)
  {
    MessageBox("发送Http请求失败!","提示",MB_ICONERROR | MB_OK);
    return FALSE;
  }

        //之后就是生成图片了。
  hFile.Open("temp.dat",CFile::modeWrite | CFile::modeCreate,NULL);
    
         InternetReadFile(hRequest,szRecvBuf,1024,&dwNumberOfBytesRead);
 
    }
1.2  怎么在VC的对话框窗口上显示出这个图片呢。
 在此结出了一种借助于COM接口的OLE方法来实现上述功能
 要使用IPicture 的COM接口,其主要的接口是IPicture和IPictureDisp,后者是由IDispatch接口派生以便通过自动化对图像的属性进行访问。图像对象也支持外部接口IPropertyNotifySink,以便用户能在图像属性发生改变时作出决定。图像对象也支持IPersistStream接口,所以它能从一个IStream接口的实例对象保存、装载自己,而IStream接口也支持对流对象的数据读写。

  我们可以用函数OleLoadPicture从包含有图像数据的流中装载图像。该函数简化了基于流的图像对象的创建过程,可以创建一个新的图像对象并且用流中的内容对它进行初始化。其函数原型为:

STDAPI OleLoadPicture( IStream * pStream, //指向包含有图像数据的流的指针
LONG lSize, //从流中读取的字节数
BOOL fRunmode, //图像属性对应的初值
REFIID riid, //涉及到的接口标识,描述要返回的接口指针的类型
VOID ppvObj // 在rrid中用到的接口指针变量的地址) ;

先将图片数据放入内存
代码:
bool CQQ号申请工具Dlg::LoadPic(CString m_path)
{
  IStream *pStm;
  CFileStatus fstatus;
  CFile file;
  LONG cb; 
  if (file.Open(m_path,CFile::modeRead)&&file.GetStatus(m_path,fstatus)&& ((cb = fstatus.m_size) != -1))
  {
    HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, cb);
     LPVOID pvData = NULL;
     if (hGlobal != NULL)
       {
        if ((pvData = GlobalLock(hGlobal)) != NULL)
          {
            file.Read(pvData, cb);
            GlobalUnlock(hGlobal);
          CreateStreamOnHGlobal(hGlobal, TRUE, &pStm);
          }
       }
  }
  return TRUE;
}

  • 标 题:答复
  • 作 者:ddsoft
  • 时 间:2011-08-29 13:47:41

为工程加一个IPicture *pPic 变量。
直接调用OleLoadPicture函数从流中装载图像:

OleLoadPicture(pStm,fstatus.m_size,TRUE,IID_IPicture,(LPVOID*)&pPic));

  由于该函数有时会导致失败,所以应当用SUCCEEDED宏来做一些适当的保护工作,只有在数据装载成功的前提下才能继续下面的图像显示工作:

代码:
int CQQ号申请工具Dlg::DrawPic(int x, int y, CDC* pDC){  if(SUCCEEDED(OleLoadPicture(pStm,fstatus.m_size,TRUE,IID_IPicture,(LPVOID*)&pPic)))  {    OLE_XSIZE_HIMETRIC hmWidth;    OLE_YSIZE_HIMETRIC hmHeight;     pPic->get_Width(&hmWidth);     pPic->get_Height(&hmHeight);    double fX,fY;    fX = (double)pDC->GetDeviceCaps(HORZRES)*(double)hmWidth/((double)pDC->GetDeviceCaps(HORZSIZE)*100.0);    fY = (double)pDC->GetDeviceCaps(VERTRES)*(double)hmHeight/((double)pDC->GetDeviceCaps(VERTSIZE)*100.0);    if(FAILED(pPic->Render(*pDC,0,0,(DWORD)fX,(DWORD)fY,0,hmHeight,hmWidth,-hmHeight,NULL))) {      AfxMessageBox(_T("渲染图像失败!"));      pPic->Release();  }      }   return 0;}
OK。测试下没有问题。第一步已经完全完成。

2.现在开始进入http协议的处理。
这里有一个非常 重要的http请求。。。之后的加密就靠它了。。

GET:  http://reg.qq.com/cgi-bin/checkconn?seed0.9770701186312112

Accept: */*
Accept-Language: zh-cn
Referer: http://reg.qq.com/
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; User-agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; http://bsalsa.com) ; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0E; .NET4.0C)
Host: reg.qq.com
Connection: Keep-Alive


seed这个单词一看就知道是随机数。这个http请求,每次它会更新两个数组。
包含13个值。 对应于网页上的所有内容。
如密码,年,月,日,验证码 等等。

我们看下这个http请求的返回内容。
g_dataArray=new Array(new Array("77201a3173562c7715387d451b6a","44440b376f4f2c4b52216a561e54","62391830754f3d775e185d653b4a","13750a2a65523d6c482238520e71","7f79093a50403a7650237a572d50","14750a2a65523d6c4822395a0679","7137083a784f6c75563e7b471964","56211a3173562c771620655d0362","44750b2d6f57206b442951695d20","77771a3865442f624f256258007f","7121083a784f2675563e7b471964","1d791a3173562c7717286d550b7a","7775183e7245076a6e06437f215c"),
new Array(9759,9745,9748,9758,9752,9747,9755,9744,9754,9750,9749,9751,9753),"/cgi-bin/getnum");

这里可能看不明白。
我将这些数据逆序,并整理下
"7775183e7245076a6e06437f215c"
"1d791a3173562c7717286d550b7a",
"7121083a784f2675563e7b471964",
"77771a3865442f624f256258007f",
"44750b2d6f57206b442951695d20",
"56211a3173562c771620655d0362",
"7137083a784f6c75563e7b471964",
"14750a2a65523d6c4822395a0679",
"7f79093a50403a7650237a572d50",
"13750a2a65523d6c482238520e71",
"62391830754f3d775e185d653b4a",
"44440b376f4f2c4b52216a561e54",
"77201a3173562c7715387d451b6a",

看看我们在网页上申请后,所提交的http请求。

  • 标 题:答复
  • 作 者:ddsoft
  • 时 间:2011-08-29 14:12:56

数据太长,我列出来。

POST : http://reg.qq.com/cgi-bin/getnum

Accept: */*
Content-Type: application/x-www-form-urlencoded
Referer: http://reg.qq.com/#
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; User-agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; http://bsalsa.com) ; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0E; .NET4.0C)
Host: reg.qq.com
Content-Length: 462
Connection: Keep-Alive

提交的数据:
7775183e7245076a6e06437f215c=ddsoft&1d791a3173562c7717286d550b7a=42&7121083a784f2675563e7b471964=pediytest&77771a3865442f624f256258007f=0&44750b2d6f57206b442951695d20=6&56211a3173562c771620655d0362=nfxnys&7137083a784f6c75563e7b471964=23&14750a2a65523d6c4822395a0679=1&7f79093a50403a7650237a572d50=1&13750a2a65523d6c482238520e71=1&62391830754f3d775e185d653b4a=2&44440b376f4f2c4b52216a561e54=pediytest&77201a3173562c7715387d451b6a=2009&qzone_flag=1&alskdjf=fjdksla
列成表格形式一目了然。


主要是发包时加密字串是固定顺序,但表单中的value 顺序与 产生的第二个数组有关。
因为第二个数据全部为数字,猜测与数字大小有关。我这里没有继续研究,有兴趣的朋友发表下你的成果。
最后,感谢你的支持!

不知能不能骗个精华。呵呵!