65.9K
CodeProject 正在变化。 阅读更多。
Home

通过加密来保护您的即时消息( IM) 对话

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.63/5 (25投票s)

2005年10月20日

6分钟阅读

viewsIcon

156494

downloadIcon

2767

了解如何加密即时通讯 (IM) 对话。

IMEncryptor: 加密您的即时通讯 (IM) 对话。

图 1. 全景图。

目录

引言

如今,每个人都知道如何使用 嗅探器,并且可能(如果通道尚未加密)找到一些敏感信息。将此类工具“隐藏”在网络内部的几个敏感区域,“潜伏”起来,有人可能会截获例如 Yahoo Messenger 的对话。这是因为(您可能知道这个事实)这些对话并未加密。

短故事

早在 2000 年,曾有一个 Yahoo Messenger 企业版。该版本使用加密来保护对话。由于大公司没有购买它(因为它不是免费的),三年后,Yahoo 解散了其企业解决方案部门

回到我们的工具,您该如何保护自己?

  1. 有一些 软件套装 可供选择,但是……您必须购买它们。
  2. 尝试找出加密对话行的可能方法。这就是我当时的想法……

特点

  • 使用 Yahoo Messenger 与朋友发送(接收)的文本消息的加密(解密)。
  • 这个初始版本(1.0 版)仅支持 3DES 加密算法。
  • 加密的消息会用星号 (*) 标记。
  • 保存在您硬盘上的消息(消息存档)将保持加密状态。消息会在您启动应用程序时解密。

图 2. 消息存档。

必备组件

拼图碎片,或者说……我们在玩什么?

  • 底层 Windows 消息(嗯……现在非常底层 - 只使用旧的 WM_* 消息)。
  • .NET 加密。

想法 - 用文字描述

正如您所见,Yahoo Messenger 窗口被划分为几个区域。这些区域就是窗口,每个窗口都有一个 hWnd(窗口句柄)。其他 Windows 软件套装不使用这种技术(例如,Office 套件)。为每个控件使用窗口句柄会消耗系统资源。在我们的案例中,这些句柄将帮助我们实现目标。

我们感兴趣的三个窗口区域是:

  1. 您键入要发送的消息的区域 - WriteArea
  2. 显示与朋友发送/接收的消息的区域 - TalkArea
  3. 我们点击发送这些消息的按钮 - SendButton

图 3. 窗口区域。

接下来,您将看到加密消息、发送加密消息以及解密收到的加密消息所需的步骤。

发送加密的消息行

  1. 从编写区域剪切消息 - hWndWriteArea
  2. 使用我们的应用程序加密它。
  3. 粘贴回编写区域,标记为待解密 - hWndWriteArea
  4. 通过模拟点击“发送”按钮发送它 - hWndSendButton

解密加密的入站消息

  1. 从对话区域获取消息 - hWndTalkArea
  2. 检查消息是否有标记。
  3. 解密标记的消息。
  4. 将这些消息粘贴回对话区域 - hWndTalkArea

想法 - 用代码描述

查找我们感兴趣的三个窗口区域的 HWND

检查前台窗口是否为 Yahoo Messenger 对话窗口。如果是,则调用 MessengerYahoo_HandleWindow 方法。此方法通过调用 MessengerYahoo_MoveTheWindow 将 IMEncryptor 窗口移动到对话窗口旁边,然后使用委托,它会遍历子窗口以查找我们感兴趣的 hWnd,即 MessengerYahoo_HandleEnumChild

提示:如果您想再次搜索子窗口,此函数应返回 true;否则,则返回 false

private bool MessengerYahoo_HandleEnumChild(
                          IntPtr hWnd, IntPtr lParam)
{
    int textLength = Win32API.SendMessage(hWnd, 
                 Win32API.WM_GETTEXTLENGTH, 0, IntPtr.Zero);
    StringBuilder text = new StringBuilder(textLength + 1);
    Win32API.SendMessage(hWnd, Win32API.WM_GETTEXT, 
                                     textLength + 1, text);
    
    StringBuilder className = new StringBuilder();
    Win32API.GetClassName(hWnd, className, 
                                    className.MaxCapacity);

    if (className.ToString().Contains(writeAreaClassName))
        hWndWriteArea = hWnd;

    if (className.ToString().Contains(talkAreaContainerName))
        hWndTalkArea = Win32API.FindWindowEx(hWnd, 
                 IntPtr.Zero, talkAreaClassName, IntPtr.Zero);

    if (className.ToString().Contains(sendButtonClassName))
        if (text.ToString().Contains(sendButtonText))
            hWndSendButton = hWnd;

    if ((hWndWriteArea == IntPtr.Zero) || 
                        (hWndTalkArea == IntPtr.Zero) || 
                        (hWndSendButton == IntPtr.Zero))
        return true;
    else
        return false;
}

writeAreaClassNametalkAreaContainerNametalkAreaClassNamesendButtonClassNamesendButtonTextFrmMain 类的开头处的 VariablesConstants 区域中定义。

#region Variables and Constants 

IntPtr hWndForegroundWindow;  // Foreground window handler
IntPtr hWndTalkArea;          // Talk area window handler
IntPtr hWndWriteArea;         // Write area window handler
IntPtr hWndSendButton;        // Send button window handler

// Yahoo Messenger's window title contains this string
const String windowTitle = "- Instant Message"; 
// The class name of the writeArea zone                
const String writeAreaClassName = "YIMInputWindow";             
// The container name of the "talkArea"
const String talkAreaContainerName = "YHTMLContainer";            
// The class name of the "talkArea"
const String talkAreaClassName = "Internet Explorer_Server";    
// The class name of the "sendButton"
const String sendButtonClassName = "Button";                    
// The text of the "sendButton"
const String sendButtonText = "&Send";                        

// Marks the START of an encrypted line of message
const String startTag = "!%";                                   
// Marks the END of an encrypted line of message
const String stopTag = "%!";                                    

// This is a decrypted line
const String decryptedLineMarker = "* ";                        

// The secret password entered by the user
static String secretPassword;                                    

#endregion

发送加密的消息行

FrmMain 类中的 MessengerYahoo_HandleSend 方法处理此问题。如您在下面的代码中所示:

  1. 我们发送 Win32Api.WM_GETTEXTLENGTH 来获取 hWndWriteArea 后面的窗口文本的长度,并发送 Win32Api.WM_GETTEXT 消息来获取明文消息。
  2. 初始化并使用对称加密提供程序 SIMCrypto(3DES),通过调用 sc.Encrypt 来加密明文消息。
  3. 使用 Win32API.WM_SETTEXT 将消息写回 hWndWriteArea 后面的窗口。
  4. 通过模拟点击发送按钮来发送加密的消息(我们向按钮发送 Win32API.BN_CLICK 消息)。

提示:通过调用 Win32API.SetForegroundWindow 将前台窗口设置回原位,用户就可以开始键入另一条消息了。

private void MessengerYahoo_HandleSend()
{
    // 1. GET the message from the writeArea
    int textLength = Win32API.SendMessage(hWndWriteArea, 
                   Win32API.WM_GETTEXTLENGTH, 0, IntPtr.Zero);
    StringBuilder writingtext = 
                            new StringBuilder(textLength + 1);
    Win32API.SendMessage(hWndWriteArea, 
            Win32API.WM_GETTEXT, textLength + 1, writingtext);

    // 2. ENCRYPT the message
    SIMCrypto sc = 
       new SIMCrypto(SIMCrypto.ServiceProviderEnum.TripleDES);
    String encryptedText = startTag + 
          sc.Encrypt(writingtext.ToString(), secretPassword) + 
          stopTag;

    // Delete the message from the writeArea
    Win32API.SendMessage(hWndWriteArea, 
          Win32API.WM_SETTEXT, 0, 
          System.Text.Encoding.ASCII.GetBytes("".ToCharArray()));

    // 3. PASTE the new encrypted message
    byte[] encryptedText_byte = 
          System.Text.Encoding.ASCII.GetBytes(
                                    encryptedText.ToCharArray());
    Win32API.SendMessage(hWndWriteArea, 
                     Win32API.WM_SETTEXT, 0, encryptedText_byte);

    // 4. SEND the new encrypted message 
    // (by simulating a click on the Send button)
    Win32API.SendMessage(hWndSendButton, 
                 Win32API.BM_CLICK, 0, encryptedText_byte);

    // Delete the message from the writeArea
    Win32API.SendMessage(hWndWriteArea, 
          Win32API.WM_SETTEXT, 0, 
          System.Text.Encoding.ASCII.GetBytes("".ToCharArray()));

    // Set the foreground window = the messeger window
    Win32API.SetForegroundWindow(hWndForegroundWindow);
}

解密加密的入站消息

FrmMain 类中的 MessengerYahoo_HandleDecryption 方法处理此问题。如您在下面的代码中所示:

  1. 我们通过向 hWndTalkArea 发送 WM_HTML_GETOBJECT 消息来获取 IHTMLDocument2 的句柄。IHTMLDocument2 被“Internet Explorer_Server”控件封装,该控件在上方定义为 talkAreaClassName。一篇关于此问题的小而有趣的短文可以在此处找到。
  2. 接下来,我们(以一种简单的方式)解析其结构以获取标记的消息。
  3. 使用相同的加密提供程序 SIMCrypto(3DES)并通过调用 sc.Decrypt 方法解密标记的消息。
  4. 将解密的消息粘贴回其应在的位置。

提示:要能够使用 IHTMLDocument2,您需要添加对名为 Microsoft.mshtml 的 .NET 组件的引用。

private void MessengerYahoo_HandleDecryption()
{
    // 1. GET the messages from the conversation area
    uint msg = 
      Win32API.RegisterWindowMessage("WM_HTML_GETOBJECT");
    IntPtr lRes = IntPtr.Zero;
    Win32API.SendMessageTimeout(hWndTalkArea, msg, 
        IntPtr.Zero, IntPtr.Zero, 
        Win32API.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 
        1000, out lRes);

    IHTMLDocument2 myHTMLDoc = 
      (mshtml.IHTMLDocument2)Win32API.ObjectFromLresult(lRes, 
               typeof(mshtml.IHTMLDocument).GUID, IntPtr.Zero);
    foreach (IHTMLElement el in (IHTMLElementCollection)myHTMLDoc.body.all)
        if ((el.GetType().ToString().
           CompareTo("mshtml.HTMLSpanElementClass") == 0) && 
                                                    (el.className != null))
        
            // 2. CHECK for the markers
            if ((el.innerText != null) && 
                 (el.innerText.IndexOf("!%") >= 0) && 
                 (el.innerText.LastIndexOf("%!") > 0))
            {
                // 3. DECRYPT the marked message
                String ts = el.innerText.ToString();
                String ts2 = ts.Substring(el.innerText.IndexOf(startTag) + 2, 
                                       el.innerText.LastIndexOf(stopTag) - 2);
                
                String decryptedText = null;
                try
                {
                    SIMCrypto sc = 
                       new SIMCrypto(SIMCrypto.ServiceProviderEnum.TripleDES);
                    decryptedText = 
                       decryptedLineMarker + sc.Decrypt(ts2, secretPassword);
                }
                catch (Exception ex)
                {
                    decryptedText = 
                            "\n" + "Received a malformed encrypted string:" + 
                            ts2 + "\n" + "Exception generated: " + ex.Message;
                }
                
                // 4. PASTE the decrypted message 
                el.innerText = decryptedText;
            }
}

如何使用 IMEncryptor

  • 下载并安装 .NET Framework 2.0 Beta 2(如果尚未安装)。
  • 下载并运行 IMEncryptor.exe 文件(您可以在 IMEncryptor_Demo.zip 中找到它)。
  • 启动 IMEncryptor,在文本框中输入您的秘密密码,然后勾选“启用加密”复选框。
  • 启动 Yahoo Messenger 对话窗口。您会看到 IMEncryptor 窗口停靠在当前对话窗口旁边。
  • 编写区域键入一条消息,键入后不要按回车键点击“发送加密消息”按钮。
  • 如果您喜欢或不喜欢这个程序,发现错误,或者想要实现其他功能,请给我发消息(或电子邮件)。

待办事项列表

  1. 进一步优化代码。
  2. 支持多种加密算法。
  3. 自动密钥交换机制。
  4. 管理多个密钥(针对多个用户)。
  5. 启用全局钩子以捕获 Enter 键?
  6. 支持其他即时通讯软件(MSN Messenger、Google Talk 等)?
  7. 您来命名功能……

版本历史

  • 2005 年 10 月 20 日 - 1.0 版。
    • 首次发布。包含主要功能。
© . All rights reserved.